1 /*
2 * Copyright (C) 2021 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 "executor/file_stream_dumper.h"
16 #include <dirent.h>
17 #include <unistd.h>
18 #include "dump_utils.h"
19
20 namespace OHOS {
21 namespace HiviewDFX {
FileStreamDumper()22 FileStreamDumper::FileStreamDumper()
23 :fp_(nullptr),
24 fd_(-1),
25 next_file_index_(0),
26 more_data_(false),
27 need_loop_(false)
28 {
29 }
30
~FileStreamDumper()31 FileStreamDumper::~FileStreamDumper()
32 {
33 CloseFd();
34 }
35
PreExecute(const std::shared_ptr<DumperParameter> & parameter,StringMatrix dumpDatas)36 DumpStatus FileStreamDumper::PreExecute(const std::shared_ptr<DumperParameter>& parameter,
37 StringMatrix dumpDatas)
38 {
39 if (dumpDatas != nullptr) {
40 result_ = dumpDatas;
41 }
42
43 // open first file!
44 if (next_file_index_ <= 0) {
45 std::string target = ptrDumpCfg_->target_;
46 if (target == "/sys/kernel/debug/binder/state") {
47 needHideAddr_ = true;
48 }
49 bool arg_pid = false;
50 bool arg_cpuid = false;
51 int pid = 0;
52 int cpuid = 0;
53 if (ptrDumpCfg_->args_.get()) {
54 arg_pid = ptrDumpCfg_->args_->HasPid();
55 arg_cpuid = ptrDumpCfg_->args_->HasCpuId();
56 pid = ptrDumpCfg_->args_->GetPid();
57 cpuid = ptrDumpCfg_->args_->GetCpuId();
58 }
59 BuildFileNames(target, arg_pid, pid, arg_cpuid, cpuid);
60 need_loop_ = (ptrDumpCfg_->loop_ == DumperConstant::LOOP);
61
62 int ret = OpenNextFile();
63 if (ret <= 0) {
64 return DumpStatus::DUMP_FAIL;
65 }
66 }
67
68 return DumpStatus::DUMP_OK;
69 }
70
OpenNextFile()71 int FileStreamDumper::OpenNextFile()
72 {
73 // Close current file.
74 CloseFd();
75 if (next_file_index_ >= filenames_.size()) {
76 // no more files
77 return 0;
78 }
79 // Next file
80 std::string filename = filenames_[next_file_index_];
81 // Open fd and fp
82 if ((fd_ = DumpUtils::FdToRead(filename)) == -1) {
83 return -1;
84 }
85 if ((fp_ = fdopen(fd_, "r")) == nullptr) {
86 close(fd_);
87 fd_ = -1;
88 return -1;
89 }
90 next_file_index_ ++;
91 // add file name into buffer
92 std::vector<std::string> line_vector_blank;
93 line_vector_blank.push_back("");
94 std::vector<std::string> line_vector_filename;
95 line_vector_filename.push_back(filename);
96 result_->push_back(line_vector_blank);
97 result_->push_back(line_vector_filename);
98 result_->push_back(line_vector_blank);
99 return next_file_index_;
100 }
101
ReplaceAddresses(const std::string & input)102 std::string FileStreamDumper::ReplaceAddresses(const std::string& input)
103 {
104 std::string result = input;
105 size_t start = result.find(": u");
106 if (start == std::string::npos) {
107 return result;
108 }
109 std::string replacement = ": u0000000000000000 c0000000000000000";
110 if (start < result.length()) {
111 result.replace(start, replacement.length(), replacement);
112 }
113 return result;
114 }
115
116 // read one line
ReadLineInFile()117 DumpStatus FileStreamDumper::ReadLineInFile()
118 {
119 DumpStatus ret = DumpStatus::DUMP_MORE_DATA;
120 if (fp_ == nullptr) {
121 return DumpStatus::DUMP_FAIL;
122 }
123 char* line_buffer = nullptr;
124 size_t len = 0;
125 ssize_t read = 0;
126 read = getline(&line_buffer, &len, fp_);
127 if (read != -1) {
128 if (line_buffer[read-1] == '\n') {
129 line_buffer[read-1] = '\0'; // replease \n
130 }
131 std::string line = line_buffer;
132 if (needHideAddr_ && DumpUtils::IsUserMode()) {
133 line = ReplaceAddresses(line);
134 }
135 std::vector<std::string> line_vector;
136 line_vector.push_back(line);
137 result_->push_back(line_vector);
138 } else {
139 if (feof(fp_) == 0) { // ferror()
140 ret = DumpStatus::DUMP_FAIL;
141 }
142 }
143 // end of file
144 if (feof(fp_) != 0) {
145 int more_file = OpenNextFile();
146 if (more_file > 0) {
147 ret = DumpStatus::DUMP_MORE_DATA;
148 } else if (more_file == 0) {
149 ret = DumpStatus::DUMP_OK;
150 } else { // Error!
151 ret = DumpStatus::DUMP_FAIL;
152 }
153 }
154 if (line_buffer != nullptr) {
155 free(line_buffer);
156 line_buffer = nullptr;
157 }
158 more_data_ = (ret == DumpStatus::DUMP_MORE_DATA);
159 return ret;
160 }
161
Execute()162 DumpStatus FileStreamDumper::Execute()
163 {
164 DumpStatus ret = DumpStatus::DUMP_OK;
165 if (need_loop_) {
166 // file dump one line
167 return ReadLineInFile();
168 } else {
169 // file dump all line
170 do {
171 if (IsCanceled()) {
172 break;
173 }
174 ret = ReadLineInFile();
175 } while (ret == DumpStatus::DUMP_MORE_DATA);
176 }
177 return ret;
178 }
179
AfterExecute()180 DumpStatus FileStreamDumper::AfterExecute()
181 {
182 if (!more_data_) {
183 CloseFd();
184 if (ptrDumpCfg_->target_ == "/sys/kernel/debug/binder/state") {
185 //The file /sys/kernel/debug/binder/state has been read, set needHideAddr_ false.
186 needHideAddr_ = false;
187 }
188 return DumpStatus::DUMP_OK;
189 }
190 return DumpStatus::DUMP_MORE_DATA;
191 }
192
BuildFileNames(const std::string & target,bool arg_pid,int pid,bool arg_cpuid,int cpuid)193 void FileStreamDumper::BuildFileNames(const std::string& target, bool arg_pid, int pid,
194 bool arg_cpuid, int cpuid)
195 {
196 DIR* dir = opendir(target.c_str());
197 if (dir != nullptr) {
198 std::string dir_name = target;
199 // add backslash at tail of target
200 std::string backslash = "/";
201 if (dir_name.compare(dir_name.size() - backslash.size(), backslash.size(), backslash) != 0) {
202 dir_name.append(backslash);
203 }
204 dirent* ptr = nullptr;
205 while ((ptr = readdir(dir)) != nullptr) {
206 std::string d_name = ptr->d_name;
207 if ((d_name == ".") || (d_name == "..")) {
208 continue;
209 }
210 std::string filename = dir_name + d_name;
211
212 if (arg_pid) {
213 ReplacePidInFilename(filename, pid);
214 }
215 if (arg_cpuid) {
216 ReplaceCpuidInFilename(filename, cpuid);
217 }
218 filenames_.push_back(filename);
219 }
220 closedir(dir);
221 } else {
222 std::string filename = target;
223 if (arg_pid) {
224 ReplacePidInFilename(filename, pid);
225 }
226 if (arg_cpuid) {
227 ReplaceCpuidInFilename(filename, cpuid);
228 }
229 filenames_.push_back(filename);
230 }
231 }
232
ReplacePidInFilename(std::string & filename,int pid)233 void FileStreamDumper::ReplacePidInFilename(std::string& filename, int pid)
234 {
235 // replease %pid in filename
236 size_t pos = filename.find("%pid");
237 if (pos != std::string::npos) {
238 filename = filename.replace(pos, strlen("%pid"), std::to_string(pid));
239 }
240 }
241
ReplaceCpuidInFilename(std::string & filename,int cpuid)242 void FileStreamDumper::ReplaceCpuidInFilename(std::string& filename, int cpuid)
243 {
244 // replease %cpuid in filename
245 size_t pos = filename.find("%cpuid");
246 if (pos != std::string::npos) {
247 filename = filename.replace(pos, strlen("%cpuid"), std::to_string(cpuid));
248 }
249 }
250
CloseFd()251 void FileStreamDumper::CloseFd()
252 {
253 if (fp_ != nullptr) {
254 fclose(fp_);
255 fp_ = nullptr;
256 fd_ = -1;
257 }
258 };
259 } // namespace HiviewDFX
260 } // namespace OHOS
261