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 template <class CallbackType>
ReadMapFileContent(char * content,const CallbackType & callback)34 bool ReadMapFileContent(char* content, const CallbackType& callback) {
35   uint64_t start_addr;
36   uint64_t end_addr;
37   uint16_t flags;
38   uint64_t pgoff;
39   ino_t inode;
40   char* next_line = content;
41   char* p;
42 
43   auto pass_space = [&]() {
44     if (*p != ' ') {
45       return false;
46     }
47     while (*p == ' ') {
48       p++;
49     }
50     return true;
51   };
52 
53   auto pass_xdigit = [&]() {
54     if (!isxdigit(*p)) {
55       return false;
56     }
57     do {
58       p++;
59     } while (isxdigit(*p));
60     return true;
61   };
62 
63   while (next_line != nullptr && *next_line != '\0') {
64     p = next_line;
65     next_line = strchr(next_line, '\n');
66     if (next_line != nullptr) {
67       *next_line = '\0';
68       next_line++;
69     }
70     // Parse line like: 00400000-00409000 r-xp 00000000 fc:00 426998  /usr/lib/gvfs/gvfsd-http
71     char* end;
72     // start_addr
73     start_addr = strtoull(p, &end, 16);
74     if (end == p || *end != '-') {
75       return false;
76     }
77     p = end + 1;
78     // end_addr
79     end_addr = strtoull(p, &end, 16);
80     if (end == p) {
81       return false;
82     }
83     p = end;
84     if (!pass_space()) {
85       return false;
86     }
87     // flags
88     flags = 0;
89     if (*p == 'r') {
90       flags |= PROT_READ;
91     } else if (*p != '-') {
92       return false;
93     }
94     p++;
95     if (*p == 'w') {
96       flags |= PROT_WRITE;
97     } else if (*p != '-') {
98       return false;
99     }
100     p++;
101     if (*p == 'x') {
102       flags |= PROT_EXEC;
103     } else if (*p != '-') {
104       return false;
105     }
106     p++;
107     if (*p != 'p' && *p != 's') {
108       return false;
109     }
110     p++;
111     if (!pass_space()) {
112       return false;
113     }
114     // pgoff
115     pgoff = strtoull(p, &end, 16);
116     if (end == p) {
117       return false;
118     }
119     p = end;
120     if (!pass_space()) {
121       return false;
122     }
123     // major:minor
124     if (!pass_xdigit() || *p++ != ':' || !pass_xdigit() || !pass_space()) {
125       return false;
126     }
127     // inode
128     inode = strtoull(p, &end, 10);
129     if (end == p) {
130       return false;
131     }
132     p = end;
133 
134     if (*p != '\0' && !pass_space()) {
135       return false;
136     }
137 
138     // filename
139     callback(start_addr, end_addr, flags, pgoff, inode, p);
140   }
141   return true;
142 }
143 
ReadMapFile(const std::string & map_file,const std::function<void (uint64_t,uint64_t,uint16_t,uint64_t,ino_t,const char *)> & callback)144 inline bool ReadMapFile(const std::string& map_file,
145                         const std::function<void(uint64_t, uint64_t, uint16_t, uint64_t, ino_t,
146                                                  const char*)>& callback) {
147   std::string content;
148   if (!android::base::ReadFileToString(map_file, &content)) {
149     return false;
150   }
151   return ReadMapFileContent(&content[0], callback);
152 }
153 
ReadProcessMaps(pid_t pid,const std::function<void (uint64_t,uint64_t,uint16_t,uint64_t,ino_t,const char *)> & callback)154 inline bool ReadProcessMaps(pid_t pid,
155                             const std::function<void(uint64_t, uint64_t, uint16_t, uint64_t, ino_t,
156                                                      const char*)>& callback) {
157   return ReadMapFile("/proc/" + std::to_string(pid) + "/maps", callback);
158 }
159 
160 struct MapInfo {
161   uint64_t start;
162   uint64_t end;
163   uint16_t flags;
164   uint64_t pgoff;
165   ino_t inode;
166   std::string name;
167 
MapInfoMapInfo168   MapInfo(uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, ino_t inode,
169           const char* name)
170       : start(start), end(end), flags(flags), pgoff(pgoff), inode(inode), name(name) {}
171 };
172 
ReadProcessMaps(pid_t pid,std::vector<MapInfo> * maps)173 inline bool ReadProcessMaps(pid_t pid, std::vector<MapInfo>* maps) {
174   return ReadProcessMaps(
175       pid, [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, ino_t inode,
176                const char* name) { maps->emplace_back(start, end, flags, pgoff, inode, name); });
177 }
178 
179 bool ReadMapFileAsyncSafe(const char* map_file, void* buffer, size_t buffer_size,
180                           const std::function<void(uint64_t, uint64_t, uint16_t, uint64_t, ino_t,
181                                                    const char*)>& callback);
182 
183 } /* namespace procinfo */
184 } /* namespace android */
185