• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #pragma once
18 
19 #include <stdlib.h>
20 #include <string.h>
21 #include <sys/mman.h>
22 #include <sys/types.h>
23 
24 #include <functional>
25 #include <string>
26 #include <vector>
27 
28 #include <android-base/file.h>
29 
30 namespace android {
31 namespace procinfo {
32 
33 struct MapInfo {
34   uint64_t start;
35   uint64_t end;
36   uint16_t flags;
37   uint64_t pgoff;
38   ino_t inode;
39   std::string name;
40   bool shared;
41 
MapInfoMapInfo42   MapInfo(uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, ino_t inode,
43           const char* name, bool shared)
44       : start(start),
45         end(end),
46         flags(flags),
47         pgoff(pgoff),
48         inode(inode),
49         name(name),
50         shared(shared) {}
51 
MapInfoMapInfo52   MapInfo(const MapInfo& params)
53       : start(params.start),
54         end(params.end),
55         flags(params.flags),
56         pgoff(params.pgoff),
57         inode(params.inode),
58         name(params.name),
59         shared(params.shared) {}
60 };
61 
62 typedef std::function<void(const MapInfo&)> MapInfoCallback;
63 typedef std::function<void(uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff,
64                       ino_t inode, const char* name, bool shared)> MapInfoParamsCallback;
65 
PassSpace(char ** p)66 static inline bool PassSpace(char** p) {
67   if (**p != ' ') {
68     return false;
69   }
70   while (**p == ' ') {
71     (*p)++;
72   }
73   return true;
74 }
75 
PassXdigit(char ** p)76 static inline bool PassXdigit(char** p) {
77   if (!isxdigit(**p)) {
78     return false;
79   }
80   do {
81     (*p)++;
82   } while (isxdigit(**p));
83   return true;
84 }
85 
86 // Parses the given line p pointing at proc/<pid>/maps content buffer and returns true on success
87 // and false on failure parsing. The first new line character of line will be replaced by the
88 // null character and *next_line will point to the character after the null.
89 //
90 // Example of how a parsed line look line:
91 // 00400000-00409000 r-xp 00000000 fc:00 426998  /usr/lib/gvfs/gvfsd-http
ParseMapsFileLine(char * p,uint64_t & start_addr,uint64_t & end_addr,uint16_t & flags,uint64_t & pgoff,ino_t & inode,char ** name,bool & shared,char ** next_line)92 static inline bool ParseMapsFileLine(char* p, uint64_t& start_addr, uint64_t& end_addr, uint16_t& flags,
93                       uint64_t& pgoff, ino_t& inode, char** name, bool& shared, char** next_line) {
94   // Make the first new line character null.
95   *next_line = strchr(p, '\n');
96   if (*next_line != nullptr) {
97     **next_line = '\0';
98     (*next_line)++;
99   }
100 
101   char* end;
102   // start_addr
103   start_addr = strtoull(p, &end, 16);
104   if (end == p || *end != '-') {
105     return false;
106   }
107   p = end + 1;
108   // end_addr
109   end_addr = strtoull(p, &end, 16);
110   if (end == p) {
111     return false;
112   }
113   p = end;
114   if (!PassSpace(&p)) {
115     return false;
116   }
117   // flags
118   flags = 0;
119   if (*p == 'r') {
120     flags |= PROT_READ;
121   } else if (*p != '-') {
122     return false;
123   }
124   p++;
125   if (*p == 'w') {
126     flags |= PROT_WRITE;
127   } else if (*p != '-') {
128     return false;
129   }
130   p++;
131   if (*p == 'x') {
132     flags |= PROT_EXEC;
133   } else if (*p != '-') {
134     return false;
135   }
136   p++;
137   if (*p != 'p' && *p != 's') {
138     return false;
139   }
140   shared = *p == 's';
141 
142   p++;
143   if (!PassSpace(&p)) {
144     return false;
145   }
146   // pgoff
147   pgoff = strtoull(p, &end, 16);
148   if (end == p) {
149     return false;
150   }
151   p = end;
152   if (!PassSpace(&p)) {
153     return false;
154   }
155   // major:minor
156   if (!PassXdigit(&p) || *p++ != ':' || !PassXdigit(&p) || !PassSpace(&p)) {
157     return false;
158   }
159   // inode
160   inode = strtoull(p, &end, 10);
161   if (end == p) {
162     return false;
163   }
164   p = end;
165 
166   if (*p != '\0' && !PassSpace(&p)) {
167     return false;
168   }
169 
170   // Assumes that the first new character was replaced with null.
171   *name = p;
172 
173   return true;
174 }
175 
ReadMapFileContent(char * content,const MapInfoParamsCallback & callback)176 inline bool ReadMapFileContent(char* content, const MapInfoParamsCallback& callback) {
177   uint64_t start_addr;
178   uint64_t end_addr;
179   uint16_t flags;
180   uint64_t pgoff;
181   ino_t inode;
182   char* line_start = content;
183   char* next_line;
184   char* name;
185   bool shared;
186 
187   while (line_start != nullptr && *line_start != '\0') {
188     bool parsed = ParseMapsFileLine(line_start, start_addr, end_addr, flags, pgoff,
189                                     inode, &name, shared, &next_line);
190     if (!parsed) {
191       return false;
192     }
193 
194     line_start = next_line;
195     callback(start_addr, end_addr, flags, pgoff, inode, name, shared);
196   }
197   return true;
198 }
199 
ReadMapFileContent(char * content,const MapInfoCallback & callback)200 inline bool ReadMapFileContent(char* content, const MapInfoCallback& callback) {
201   uint64_t start_addr;
202   uint64_t end_addr;
203   uint16_t flags;
204   uint64_t pgoff;
205   ino_t inode;
206   char* line_start = content;
207   char* next_line;
208   char* name;
209   bool shared;
210 
211   while (line_start != nullptr && *line_start != '\0') {
212     bool parsed = ParseMapsFileLine(line_start, start_addr, end_addr, flags, pgoff,
213                                     inode, &name, shared, &next_line);
214     if (!parsed) {
215       return false;
216     }
217 
218     line_start = next_line;
219     callback(MapInfo(start_addr, end_addr, flags, pgoff, inode, name, shared));
220   }
221   return true;
222 }
223 
ReadMapFile(const std::string & map_file,const MapInfoCallback & callback)224 inline bool ReadMapFile(const std::string& map_file,
225                 const MapInfoCallback& callback) {
226   std::string content;
227   if (!android::base::ReadFileToString(map_file, &content)) {
228     return false;
229   }
230   return ReadMapFileContent(&content[0], callback);
231 }
232 
233 
ReadMapFile(const std::string & map_file,const MapInfoParamsCallback & callback,std::string & mapsBuffer)234 inline bool ReadMapFile(const std::string& map_file, const MapInfoParamsCallback& callback,
235                         std::string& mapsBuffer) {
236   if (!android::base::ReadFileToString(map_file, &mapsBuffer)) {
237     return false;
238   }
239   return ReadMapFileContent(&mapsBuffer[0], callback);
240 }
241 
ReadMapFile(const std::string & map_file,const MapInfoParamsCallback & callback)242 inline bool ReadMapFile(const std::string& map_file,
243                 const MapInfoParamsCallback& callback) {
244   std::string content;
245   return ReadMapFile(map_file, callback, content);
246 }
247 
ReadProcessMaps(pid_t pid,const MapInfoCallback & callback)248 inline bool ReadProcessMaps(pid_t pid, const MapInfoCallback& callback) {
249   return ReadMapFile("/proc/" + std::to_string(pid) + "/maps", callback);
250 }
251 
ReadProcessMaps(pid_t pid,const MapInfoParamsCallback & callback,std::string & mapsBuffer)252 inline bool ReadProcessMaps(pid_t pid, const MapInfoParamsCallback& callback,
253                             std::string& mapsBuffer) {
254   return ReadMapFile("/proc/" + std::to_string(pid) + "/maps", callback, mapsBuffer);
255 }
256 
ReadProcessMaps(pid_t pid,const MapInfoParamsCallback & callback)257 inline bool ReadProcessMaps(pid_t pid, const MapInfoParamsCallback& callback) {
258   std::string content;
259   return ReadProcessMaps(pid, callback, content);
260 }
261 
ReadProcessMaps(pid_t pid,std::vector<MapInfo> * maps)262 inline bool ReadProcessMaps(pid_t pid, std::vector<MapInfo>* maps) {
263   return ReadProcessMaps(pid, [&](const MapInfo& mapinfo) { maps->emplace_back(mapinfo); });
264 }
265 
266 // Reads maps file and executes given callback for each mapping
267 // Warning: buffer should not be modified asynchronously while this function executes
268 template <class CallbackType>
ReadMapFileAsyncSafe(const char * map_file,void * buffer,size_t buffer_size,const CallbackType & callback)269 inline bool ReadMapFileAsyncSafe(const char* map_file, void* buffer, size_t buffer_size,
270                                  const CallbackType& callback) {
271   if (buffer == nullptr || buffer_size == 0) {
272     return false;
273   }
274 
275   int fd = open(map_file, O_RDONLY | O_CLOEXEC);
276   if (fd == -1) {
277     return false;
278   }
279 
280   char* char_buffer = reinterpret_cast<char*>(buffer);
281   size_t start = 0;
282   size_t read_bytes = 0;
283   char* line = nullptr;
284   bool read_complete = false;
285   while (true) {
286     ssize_t bytes =
287         TEMP_FAILURE_RETRY(read(fd, char_buffer + read_bytes, buffer_size - read_bytes - 1));
288     if (bytes <= 0) {
289       if (read_bytes == 0) {
290         close(fd);
291         return bytes == 0;
292       }
293       // Treat the last piece of data as the last line.
294       char_buffer[start + read_bytes] = '\n';
295       bytes = 1;
296       read_complete = true;
297     }
298     read_bytes += bytes;
299 
300     while (read_bytes > 0) {
301       char* newline = reinterpret_cast<char*>(memchr(&char_buffer[start], '\n', read_bytes));
302       if (newline == nullptr) {
303         break;
304       }
305       *newline = '\0';
306       line = &char_buffer[start];
307       start = newline - char_buffer + 1;
308       read_bytes -= newline - line + 1;
309 
310       // Ignore the return code, errors are okay.
311       ReadMapFileContent(line, callback);
312     }
313 
314     if (read_complete) {
315       close(fd);
316       return true;
317     }
318 
319     if (start == 0 && read_bytes == buffer_size - 1) {
320       // The buffer provided is too small to contain this line, give up
321       // and indicate failure.
322       close(fd);
323       return false;
324     }
325 
326     // Copy any leftover data to the front  of the buffer.
327     if (start > 0) {
328       if (read_bytes > 0) {
329         memmove(char_buffer, &char_buffer[start], read_bytes);
330       }
331       start = 0;
332     }
333   }
334 }
335 
336 } /* namespace procinfo */
337 } /* namespace android */
338