1
2 /*
3 * Copyright (c) 2024 Huawei Device Co., Ltd.
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 #include <iostream>
18 #include <fstream>
19 #include <sstream>
20 #include <unistd.h>
21 #include <fcntl.h>
22 #include <vector>
23 #include <charconv>
24
25 using namespace std;
26
27 constexpr uint64_t PFN_MASK = ((1ULL << 55) - 1);
28 constexpr uint64_t PAGE_SIZE = 1024 * 4;
29 constexpr int ARG_MINIMUM = 2;
30 constexpr int IN_RAM_OFFSET = 63;
31 constexpr int IN_SWAP_OFFSET = 54;
32 constexpr int SHARED_OFFSET = 53;
33 constexpr int EXCLUSIVE_OFFSET = 52;
34 constexpr int SOFTDIRTY_OFFSET = 51;
35 struct MapInfo {
36 uint64_t startAddr; // 起始地址
37 uint64_t endAddr; // 结束地址
38 char read;
39 char write;
40 char execute;
41 char shared;
42 uint64_t offset; // 文件偏移量
43 string dev; // 设备号
44 string inode; // inode 号
45 std::string pathname; // 文件路径
46 };
47
48 struct PageInfo {
49 unsigned int inRam;
50 unsigned int inSwap;
51 unsigned int shared;
52 unsigned int exclusive;
53 unsigned int softdirty;
54 unsigned long pfn;
55 uint64_t address;
56 };
57
58 namespace {
PrintUsage(const string & program)59 void PrintUsage(const string& program)
60 {
61 cout << "Usage: " << program << " pid" <<endl;
62 }
63
ParseMapsLine(const string & line,MapInfo & mapping)64 int ParseMapsLine(const string& line, MapInfo& mapping)
65 {
66 std::istringstream iss(line);
67 uint64_t start = 0;
68 uint64_t end = 0;
69
70 // 读取起始地址和结束地址
71 if (!(iss >> hex >> start)) {
72 return -1;
73 }
74 iss.ignore(1); // 忽略 '-'
75 if (!(iss >> hex >> end)) {
76 return -1;
77 }
78 mapping.startAddr = start;
79 mapping.endAddr = end;
80
81 // 读取权限并转换为整数
82 iss >> mapping.read;
83 iss >> mapping.write;
84 iss >> mapping.execute;
85 iss >> mapping.shared;
86 // 读取偏移量
87 if (!(iss >> mapping.offset)) {
88 return -1;
89 }
90 // 读取设备号
91 if (!(iss >> mapping.dev)) {
92 return -1;
93 }
94 // 读取 inode 号
95 if (!(iss >> mapping.inode)) {
96 return -1;
97 }
98 // 读取文件路径
99 if (!getline(iss, mapping.pathname)) {
100 mapping.pathname = "[anno]";
101 };
102 return 0;
103 }
104
ParsePagemap(uint64_t entry,PageInfo & pginfo)105 void ParsePagemap(uint64_t entry, PageInfo & pginfo)
106 {
107 pginfo.inRam = (entry >> IN_RAM_OFFSET) & 0x1;
108 pginfo.inSwap = (entry >> IN_SWAP_OFFSET) & 0x1;
109 pginfo.shared = (entry >> SHARED_OFFSET) & 0x1;
110 pginfo.exclusive = (entry >> EXCLUSIVE_OFFSET) & 0x1;
111 pginfo.softdirty = (entry >> SOFTDIRTY_OFFSET) & 0x1;
112 pginfo.pfn = entry & PFN_MASK;
113 }
114
PrintPage(const MapInfo & mapping,const PageInfo & page)115 void PrintPage(const MapInfo& mapping, const PageInfo& page)
116 {
117 cout << hex << page.address << '-' << hex << (page.address + PAGE_SIZE) << " ";
118 cout << mapping.read << mapping.write << mapping.execute << mapping.shared << " ";
119 if (page.inRam) {
120 cout << hex << page.pfn;
121 } else if (page.inSwap) {
122 cout << "[in swap]";
123 } else {
124 cout << "[not present]";
125 }
126 cout<< " " << mapping.pathname << endl;
127 }
128
IsValidPid(const string & pid_str)129 bool IsValidPid(const string& pid_str)
130 {
131 if (pid_str.empty()) {
132 return false;
133 }
134 bool ret = all_of(pid_str.begin(), pid_str.end(), [](char c) {
135 return isdigit(c);
136 });
137 return ret;
138 }
139 } // namespace
140
main(int argc,char * argv[])141 int main(int argc, char* argv[])
142 {
143 if (argc != ARG_MINIMUM) {
144 PrintUsage(argv[0]);
145 return -1;
146 }
147 string pid_str = argv[1];
148 if (!IsValidPid(pid_str)) {
149 PrintUsage(argv[0]);
150 return -1;
151 }
152 int pid = -1;
153 auto result = std::from_chars(pid_str.data(), pid_str.data() + pid_str.size(), pid);
154 if (result.ec != std::errc()) {
155 PrintUsage(argv[0]);
156 return -1;
157 }
158 string mapsPath = "/proc/" + to_string(pid) + "/maps";
159 ifstream maps_file(mapsPath, ios::binary);
160 if (!maps_file) {
161 cerr << "Failed to open maps file" << endl;
162 return -1;
163 }
164
165 string pagemapPath = "/proc/" + to_string(pid) + "/pagemap";
166 int pagemapFd = open(pagemapPath.c_str(), O_RDONLY);
167 if (pagemapFd == -1) {
168 perror("Error opening file");
169 return -1;
170 }
171 cout << "Address Range\t" << "Permissions\t" << "PFN\t" << "Path" << endl;
172 string line;
173 while (getline(maps_file, line)) {
174 MapInfo mapping;
175 bool ret = ParseMapsLine(line, mapping);
176 if (ret != 0) {
177 close(pagemapFd);
178 return ret;
179 }
180 for (uint64_t tmpAddr = mapping.startAddr; tmpAddr < mapping.endAddr; tmpAddr += PAGE_SIZE) {
181 // 计算文件中要读取的偏移量
182 uint64_t offset = (tmpAddr / PAGE_SIZE) * sizeof(unsigned long long);
183 uint64_t entry;
184 if (pread(pagemapFd, &entry, sizeof(entry), offset) != sizeof(entry)) {
185 perror("pread");
186 break;
187 }
188 PageInfo page;
189 ParsePagemap(entry, page);
190 page.address = tmpAddr;
191 PrintPage(mapping, page);
192 }
193 }
194 maps_file.close();
195 close(pagemapFd);
196 return 0;
197 }