1 /*
2 * Copyright (c) 2025 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15 #include "decorative_dump_info.h"
16 #include <dirent.h>
17 #include "dfx_util.h"
18 #include "dfx_dump_request.h"
19 #include "dfx_process.h"
20 #include "unwinder.h"
21 #include "dfx_trace.h"
22 #include "dfx_buffer_writer.h"
23 #include "dfx_log.h"
24 namespace OHOS {
25 namespace HiviewDFX {
26 namespace {
27 #define FD_TABLE_SIZE 128
28 const int ARG_MAX_NUM = 131072;
29
30 struct FdEntry {
31 _Atomic(uint64_t) close_tag;
32 _Atomic(char) signal_flag;
33 };
34
35 struct FdTableOverflow {
36 size_t len;
37 struct FdEntry entries[];
38 };
39
40 struct FdTable {
41 _Atomic(enum fdsan_error_level) error_level;
42 struct FdEntry entries[FD_TABLE_SIZE];
43 _Atomic(struct FdTableOverflow*) overflow;
44 };
45 }
46 REGISTER_DUMP_INFO_CLASS(OpenFiles);
47
Print(DfxProcess & process,const ProcessDumpRequest & request,Unwinder & unwinder)48 void OpenFiles::Print(DfxProcess& process, const ProcessDumpRequest& request, Unwinder& unwinder)
49 {
50 DecorativeDumpInfo::Print(process, request, unwinder);
51 DFX_TRACE_SCOPED("GetOpenFiles");
52 OpenFilesList openFies;
53 CollectOpenFiles(openFies, process.GetProcessInfo().pid);
54 FillFdsaninfo(openFies, process.GetProcessInfo().nsPid, request.fdTableAddr);
55 DfxBufferWriter::GetInstance().WriteMsg(DumpOpenFiles(openFies));
56 DFXLOGI("get open files info finish");
57 }
58
ReadLink(std::string & src,std::string & dst)59 bool OpenFiles::ReadLink(std::string &src, std::string &dst)
60 {
61 char buf[PATH_MAX];
62 ssize_t count = readlink(src.c_str(), buf, sizeof(buf) - 1);
63 if (count < 0) {
64 return false;
65 }
66 buf[count] = '\0';
67 dst = buf;
68 return true;
69 }
70
CollectOpenFiles(OpenFilesList & list,pid_t pid)71 void OpenFiles::CollectOpenFiles(OpenFilesList &list, pid_t pid)
72 {
73 std::string fdDirName = "/proc/" + std::to_string(pid) + "/fd";
74 std::unique_ptr<DIR, int (*)(DIR *)> dir(opendir(fdDirName.c_str()), closedir);
75 if (dir == nullptr) {
76 DFXLOGE("failed to open directory %{public}s: %{public}s", fdDirName.c_str(), strerror(errno));
77 return;
78 }
79
80 struct dirent *de;
81 while ((de = readdir(dir.get())) != nullptr) {
82 if (*de->d_name == '.') {
83 continue;
84 }
85
86 int fd = atoi(de->d_name);
87 std::string path = fdDirName + "/" + std::string(de->d_name);
88 std::string target;
89 if (ReadLink(path, target)) {
90 list[fd].path = target;
91 } else {
92 list[fd].path = "???";
93 DFXLOGE("failed to readlink %{public}s: %{public}s", path.c_str(), strerror(errno));
94 }
95 }
96 }
97
FillFdsaninfo(OpenFilesList & list,pid_t nsPid,uint64_t fdTableAddr)98 void OpenFiles::FillFdsaninfo(OpenFilesList &list, pid_t nsPid, uint64_t fdTableAddr)
99 {
100 constexpr size_t fds = sizeof(FdTable::entries) / sizeof(*FdTable::entries);
101 size_t entryOffset = offsetof(FdTable, entries);
102 uint64_t addr = fdTableAddr + entryOffset;
103 FdEntry entrys[fds];
104 if (ReadProcMemByPid(nsPid, addr, entrys, sizeof(FdEntry) * fds) != sizeof(FdEntry) * fds) {
105 DFXLOGE("[%{public}d]: read nsPid mem error %{public}s", __LINE__, strerror(errno));
106 return;
107 }
108 for (size_t i = 0; i < fds; i++) {
109 if (entrys[i].close_tag) {
110 list[i].fdsanOwner = entrys[i].close_tag;
111 }
112 }
113
114 size_t overflowOffset = offsetof(FdTable, overflow);
115 uintptr_t overflow = 0;
116 uint64_t tmp = fdTableAddr + overflowOffset;
117 if (ReadProcMemByPid(nsPid, tmp, &overflow, sizeof(overflow)) != sizeof(overflow)) {
118 return;
119 }
120 if (!overflow) {
121 return;
122 }
123
124 size_t overflowLength;
125 if (ReadProcMemByPid(nsPid, overflow, &overflowLength, sizeof(overflowLength)) != sizeof(overflowLength)) {
126 return;
127 }
128 if (overflowLength > ARG_MAX_NUM) {
129 return;
130 }
131
132 std::vector<FdEntry> overflowFdEntrys(overflowLength);
133 uint64_t address = overflow + offsetof(FdTableOverflow, entries);
134 if (ReadProcMemByPid(nsPid, address, overflowFdEntrys.data(), sizeof(FdEntry) * overflowLength) !=
135 sizeof(FdEntry) * overflowLength) {
136 DFXLOGE("[%{public}d]: read nsPid mem error %{public}s", __LINE__, strerror(errno));
137 return;
138 }
139 size_t fdIndex = fds;
140 for (size_t i = 0; i < overflowLength; i++) {
141 if (overflowFdEntrys[i].close_tag) {
142 list[fdIndex].fdsanOwner = overflowFdEntrys[i].close_tag;
143 }
144 fdIndex++;
145 }
146 }
147
DumpOpenFiles(OpenFilesList files)148 std::string OpenFiles::DumpOpenFiles(OpenFilesList files)
149 {
150 std::string openFilesStr = "OpenFiles:\n";
151 for (const auto &filePath: files) {
152 const std::string path = filePath.second.path;
153 uint64_t tag = filePath.second.fdsanOwner;
154 const char* type = fdsan_get_tag_type(tag);
155 uint64_t val = fdsan_get_tag_value(tag);
156 if (!path.empty()) {
157 openFilesStr += std::to_string(filePath.first) + "->" + path + " " + type + " " +
158 std::to_string(val) + "\n";
159 } else {
160 openFilesStr += "OpenFilesList contain an entry (fd " +
161 std::to_string(filePath.first) + ") with no path or owner\n";
162 }
163 }
164 return openFilesStr;
165 }
166 }
167 }