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