Home | History | Annotate | Download | only in trace_event
      1 // Copyright 2015 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "base/trace_event/process_memory_maps_dump_provider.h"
      6 
      7 #include <stdint.h>
      8 
      9 #include "base/files/scoped_file.h"
     10 #include "base/format_macros.h"
     11 #include "base/logging.h"
     12 #include "base/strings/string_util.h"
     13 #include "base/trace_event/process_memory_dump.h"
     14 #include "base/trace_event/process_memory_maps.h"
     15 
     16 namespace base {
     17 namespace trace_event {
     18 
     19 // static
     20 FILE* ProcessMemoryMapsDumpProvider::proc_smaps_for_testing = nullptr;
     21 
     22 namespace {
     23 
     24 const uint32_t kMaxLineSize = 4096;
     25 
     26 bool ParseSmapsHeader(const char* header_line,
     27                       ProcessMemoryMaps::VMRegion* region) {
     28   // e.g., "00400000-00421000 r-xp 00000000 fc:01 1234  /foo.so\n"
     29   bool res = true;  // Whether this region should be appended or skipped.
     30   uint64_t end_addr = 0;
     31   char protection_flags[5] = {0};
     32   char mapped_file[kMaxLineSize];
     33 
     34   if (sscanf(header_line, "%" SCNx64 "-%" SCNx64 " %4c %*s %*s %*s%4095[^\n]\n",
     35              &region->start_address, &end_addr, protection_flags,
     36              mapped_file) != 4)
     37     return false;
     38 
     39   if (end_addr > region->start_address) {
     40     region->size_in_bytes = end_addr - region->start_address;
     41   } else {
     42     // This is not just paranoia, it can actually happen (See crbug.com/461237).
     43     region->size_in_bytes = 0;
     44     res = false;
     45   }
     46 
     47   region->protection_flags = 0;
     48   if (protection_flags[0] == 'r') {
     49     region->protection_flags |=
     50         ProcessMemoryMaps::VMRegion::kProtectionFlagsRead;
     51   }
     52   if (protection_flags[1] == 'w') {
     53     region->protection_flags |=
     54         ProcessMemoryMaps::VMRegion::kProtectionFlagsWrite;
     55   }
     56   if (protection_flags[2] == 'x') {
     57     region->protection_flags |=
     58         ProcessMemoryMaps::VMRegion::kProtectionFlagsExec;
     59   }
     60 
     61   region->mapped_file = mapped_file;
     62   TrimWhitespaceASCII(region->mapped_file, TRIM_ALL, &region->mapped_file);
     63 
     64   return res;
     65 }
     66 
     67 uint64_t ReadCounterBytes(char* counter_line) {
     68   uint64_t counter_value = 0;
     69   int res = sscanf(counter_line, "%*s %" SCNu64 " kB", &counter_value);
     70   DCHECK_EQ(1, res);
     71   return counter_value * 1024;
     72 }
     73 
     74 uint32_t ParseSmapsCounter(char* counter_line,
     75                            ProcessMemoryMaps::VMRegion* region) {
     76   // A smaps counter lines looks as follows: "RSS:  0 Kb\n"
     77   uint32_t res = 1;
     78   char counter_name[20];
     79   int did_read = sscanf(counter_line, "%19[^\n ]", counter_name);
     80   DCHECK_EQ(1, did_read);
     81 
     82   if (strcmp(counter_name, "Pss:") == 0) {
     83     region->byte_stats_proportional_resident = ReadCounterBytes(counter_line);
     84   } else if (strcmp(counter_name, "Private_Dirty:") == 0) {
     85     region->byte_stats_private_dirty_resident = ReadCounterBytes(counter_line);
     86   } else if (strcmp(counter_name, "Private_Clean:") == 0) {
     87     region->byte_stats_private_clean_resident = ReadCounterBytes(counter_line);
     88   } else if (strcmp(counter_name, "Shared_Dirty:") == 0) {
     89     region->byte_stats_shared_dirty_resident = ReadCounterBytes(counter_line);
     90   } else if (strcmp(counter_name, "Shared_Clean:") == 0) {
     91     region->byte_stats_shared_clean_resident = ReadCounterBytes(counter_line);
     92   } else if (strcmp(counter_name, "Swap:") == 0) {
     93     region->byte_stats_swapped = ReadCounterBytes(counter_line);
     94   } else {
     95     res = 0;
     96   }
     97 
     98   return res;
     99 }
    100 
    101 uint32_t ReadLinuxProcSmapsFile(FILE* smaps_file, ProcessMemoryMaps* pmm) {
    102   if (!smaps_file)
    103     return 0;
    104 
    105   fseek(smaps_file, 0, SEEK_SET);
    106 
    107   char line[kMaxLineSize];
    108   const uint32_t kNumExpectedCountersPerRegion = 6;
    109   uint32_t counters_parsed_for_current_region = 0;
    110   uint32_t num_valid_regions = 0;
    111   ProcessMemoryMaps::VMRegion region;
    112   bool should_add_current_region = false;
    113   for (;;) {
    114     line[0] = '\0';
    115     if (fgets(line, kMaxLineSize, smaps_file) == nullptr)
    116       break;
    117     DCHECK_GT(strlen(line), 0u);
    118     if (isxdigit(line[0]) && !isupper(line[0])) {
    119       region = ProcessMemoryMaps::VMRegion();
    120       counters_parsed_for_current_region = 0;
    121       should_add_current_region = ParseSmapsHeader(line, &region);
    122     } else {
    123       counters_parsed_for_current_region += ParseSmapsCounter(line, &region);
    124       DCHECK_LE(counters_parsed_for_current_region,
    125                 kNumExpectedCountersPerRegion);
    126       if (counters_parsed_for_current_region == kNumExpectedCountersPerRegion) {
    127         if (should_add_current_region) {
    128           pmm->AddVMRegion(region);
    129           ++num_valid_regions;
    130           should_add_current_region = false;
    131         }
    132       }
    133     }
    134   }
    135   return num_valid_regions;
    136 }
    137 
    138 }  // namespace
    139 
    140 // static
    141 ProcessMemoryMapsDumpProvider* ProcessMemoryMapsDumpProvider::GetInstance() {
    142   return Singleton<ProcessMemoryMapsDumpProvider,
    143                    LeakySingletonTraits<ProcessMemoryMapsDumpProvider>>::get();
    144 }
    145 
    146 ProcessMemoryMapsDumpProvider::ProcessMemoryMapsDumpProvider() {
    147 }
    148 
    149 ProcessMemoryMapsDumpProvider::~ProcessMemoryMapsDumpProvider() {
    150 }
    151 
    152 // Called at trace dump point time. Creates a snapshot of the memory maps for
    153 // the current process.
    154 bool ProcessMemoryMapsDumpProvider::OnMemoryDump(const MemoryDumpArgs& args,
    155                                                  ProcessMemoryDump* pmd) {
    156   // Snapshot of memory maps is not taken for light dump requests.
    157   if (args.level_of_detail == MemoryDumpLevelOfDetail::LIGHT)
    158     return true;
    159 
    160   uint32_t res = 0;
    161   if (UNLIKELY(proc_smaps_for_testing)) {
    162     res = ReadLinuxProcSmapsFile(proc_smaps_for_testing, pmd->process_mmaps());
    163   } else {
    164     ScopedFILE smaps_file(fopen("/proc/self/smaps", "r"));
    165     res = ReadLinuxProcSmapsFile(smaps_file.get(), pmd->process_mmaps());
    166   }
    167 
    168   if (res > 0) {
    169     pmd->set_has_process_mmaps();
    170     return true;
    171   }
    172   return false;
    173 }
    174 
    175 }  // namespace trace_event
    176 }  // namespace base
    177