1 // Copyright (c) 2013 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.
5 #include "base/debug/proc_maps_linux.h"
13 #include "base/file_util.h"
14 #include "base/strings/string_split.h"
16 #if defined(OS_ANDROID)
17 // Bionic's inttypes.h defines PRI/SCNxPTR as an unsigned long int, which
18 // is incompatible with Bionic's stdint.h defining uintptr_t as a unsigned int:
19 // https://code.google.com/p/android/issues/detail?id=57218
27 // Scans |proc_maps| starting from |pos| returning true if the gate VMA was
28 // found, otherwise returns false.
29 static bool ContainsGateVMA(std::string
* proc_maps
, size_t pos
) {
30 #if defined(ARCH_CPU_ARM_FAMILY)
31 // The gate VMA on ARM kernels is the interrupt vectors page.
32 return proc_maps
->find(" [vectors]\n", pos
) != std::string::npos
;
33 #elif defined(ARCH_CPU_X86_64)
34 // The gate VMA on x86 64-bit kernels is the virtual system call page.
35 return proc_maps
->find(" [vsyscall]\n", pos
) != std::string::npos
;
37 // Otherwise assume there is no gate VMA in which case we shouldn't
38 // get duplicate entires.
43 bool ReadProcMaps(std::string
* proc_maps
) {
44 // seq_file only writes out a page-sized amount on each call. Refer to header
46 const long kReadSize
= sysconf(_SC_PAGESIZE
);
48 int fd
= HANDLE_EINTR(open("/proc/self/maps", O_RDONLY
));
50 DPLOG(ERROR
) << "Couldn't open /proc/self/maps";
53 file_util::ScopedFD
fd_closer(&fd
);
57 // To avoid a copy, resize |proc_maps| so read() can write directly into it.
58 // Compute |buffer| afterwards since resize() may reallocate.
59 size_t pos
= proc_maps
->size();
60 proc_maps
->resize(pos
+ kReadSize
);
61 void* buffer
= &(*proc_maps
)[pos
];
63 ssize_t bytes_read
= HANDLE_EINTR(read(fd
, buffer
, kReadSize
));
65 DPLOG(ERROR
) << "Couldn't read /proc/self/maps";
70 // ... and don't forget to trim off excess bytes.
71 proc_maps
->resize(pos
+ bytes_read
);
76 // The gate VMA is handled as a special case after seq_file has finished
77 // iterating through all entries in the virtual memory table.
79 // Unfortunately, if additional entries are added at this point in time
80 // seq_file gets confused and the next call to read() will return duplicate
81 // entries including the gate VMA again.
83 // Avoid this by searching for the gate VMA and breaking early.
84 if (ContainsGateVMA(proc_maps
, pos
))
91 bool ParseProcMaps(const std::string
& input
,
92 std::vector
<MappedMemoryRegion
>* regions_out
) {
93 std::vector
<MappedMemoryRegion
> regions
;
95 // This isn't async safe nor terribly efficient, but it doesn't need to be at
96 // this point in time.
97 std::vector
<std::string
> lines
;
98 SplitString(input
, '\n', &lines
);
100 for (size_t i
= 0; i
< lines
.size(); ++i
) {
101 // Due to splitting on '\n' the last line should be empty.
102 if (i
== lines
.size() - 1) {
103 if (!lines
[i
].empty())
108 MappedMemoryRegion region
;
109 const char* line
= lines
[i
].c_str();
110 char permissions
[5] = {'\0'}; // Ensure NUL-terminated string.
116 // Sample format from man 5 proc:
118 // address perms offset dev inode pathname
119 // 08048000-08056000 r-xp 00000000 03:0c 64593 /usr/sbin/gpm
121 // The final %n term captures the offset in the input string, which is used
122 // to determine the path name. It *does not* increment the return value.
123 // Refer to man 3 sscanf for details.
124 if (sscanf(line
, "%" SCNxPTR
"-%" SCNxPTR
" %4c %llx %hhx:%hhx %ld %n",
125 ®ion
.start
, ®ion
.end
, permissions
, ®ion
.offset
,
126 &dev_major
, &dev_minor
, &inode
, &path_index
) < 7) {
130 region
.permissions
= 0;
132 if (permissions
[0] == 'r')
133 region
.permissions
|= MappedMemoryRegion::READ
;
134 else if (permissions
[0] != '-')
137 if (permissions
[1] == 'w')
138 region
.permissions
|= MappedMemoryRegion::WRITE
;
139 else if (permissions
[1] != '-')
142 if (permissions
[2] == 'x')
143 region
.permissions
|= MappedMemoryRegion::EXECUTE
;
144 else if (permissions
[2] != '-')
147 if (permissions
[3] == 'p')
148 region
.permissions
|= MappedMemoryRegion::PRIVATE
;
149 else if (permissions
[3] != 's' && permissions
[3] != 'S') // Shared memory.
152 // Pushing then assigning saves us a string copy.
153 regions
.push_back(region
);
154 regions
.back().path
.assign(line
+ path_index
);
157 regions_out
->swap(regions
);