1 /*
2 * Copyright (c) Huawei Technologies Co., Ltd. 2021. All rights reserved.
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 #define HILOG_TAG "RuntimeThread"
16
17 #include "virtual_thread.h"
18
19 #include <cinttypes>
20 #include <iostream>
21 #include <sstream>
22 #if !is_mingw
23 #include <sys/mman.h>
24 #endif
25
26 #include "common.h"
27 #include "symbols_file.h"
28 #include "utilities.h"
29 #include "virtual_runtime.h"
30 namespace OHOS {
31 namespace Developtools {
32 namespace NativeDaemon {
33 #ifdef DEBUG_TIME
IsSorted() const34 bool VirtualThread::IsSorted() const
35 {
36 for (std::size_t index = 1; index < maps_->size(); ++index) {
37 if ((*maps_)[index - 1].end > (*maps_)[index].begin) {
38 std::cout << "maps_ order error:\n"
39 << " " << (*maps_)[index - 1].begin << "-" << (*maps_)[index - 1].end
40 << " " << (*maps_)[index].begin << "-" << (*maps_)[index].end;
41 return false;
42 }
43 }
44 return true;
45 }
46 #endif
47
FindMemMapsByAddr(uint64_t addr) const48 const std::pair<std::shared_ptr<MemMaps>, uint32_t> VirtualThread::FindMemMapsByAddr(uint64_t addr) const
49 {
50 return virtualruntime_->FindMap(addr);
51 }
52
FindMapByAddr(uint64_t addr) const53 const std::shared_ptr<DfxMap> VirtualThread::FindMapByAddr(uint64_t addr) const
54 {
55 HLOGM("try found vaddr 0x%" PRIx64 " in maps %zu ", addr, maps_->size());
56 if (maps_->size() == 0) {
57 return nullptr;
58 }
59 if (maps_->front()->begin > addr) {
60 return nullptr;
61 }
62 if (maps_->back()->end <= addr) {
63 return nullptr;
64 }
65 constexpr int two {2};
66 std::size_t left {0};
67 std::size_t right {maps_->size()};
68 std::size_t mid = (right - left) / two + left;
69 while (left < right) {
70 if (addr < (*maps_)[mid]->end) {
71 right = mid;
72 mid = (right - left) / two + left;
73 continue;
74 }
75 if (addr >= (*maps_)[mid]->end) {
76 left = mid + 1;
77 mid = (right - left) / two + left;
78 continue;
79 }
80 }
81 if (addr >= (*maps_)[left]->begin and addr < (*maps_)[left]->end) {
82 if (left > 0) {
83 (*maps_)[left]->prevMap = (*maps_)[left - 1];
84 }
85 return (*maps_)[left];
86 }
87 return nullptr;
88 }
VirtualThread(pid_t pid,pid_t tid,const std::unordered_map<std::string,std::unique_ptr<SymbolsFile>> & symbolsFiles,VirtualRuntime * runtime,bool parseFlag)89 VirtualThread::VirtualThread(pid_t pid,
90 pid_t tid,
91 const std::unordered_map<std::string, std::unique_ptr<SymbolsFile>>& symbolsFiles,
92 VirtualRuntime* runtime,
93 bool parseFlag)
94 : pid_(pid), tid_(tid), symbolsFiles_(symbolsFiles), virtualruntime_(runtime)
95 {
96 maps_ = &virtualruntime_->processMaps_;
97 if (parseFlag) {
98 if (virtualruntime_->processMaps_.size() == 0) {
99 this->ParseMap(virtualruntime_->processMaps_);
100 }
101 }
102
103 this->name_ = ReadThreadName(pid);
104 HLOGM("%d %d map from parent size is %zu", pid, tid, maps_->size());
105 }
106
ReadThreadName(pid_t tid)107 std::string VirtualThread::ReadThreadName(pid_t tid)
108 {
109 std::string comm = ReadFileToString(StringPrintf("/proc/%d/comm", tid)).c_str();
110 comm.erase(std::remove(comm.begin(), comm.end(), '\r'), comm.end());
111 comm.erase(std::remove(comm.begin(), comm.end(), '\n'), comm.end());
112 return comm;
113 }
114
FindMapByFileInfo(const std::string name,uint64_t offset) const115 const std::shared_ptr<DfxMap> VirtualThread::FindMapByFileInfo(const std::string name, uint64_t offset) const
116 {
117 for (auto map : *maps_) {
118 if (name != map->name) {
119 continue;
120 }
121 // check begin and length
122 if (offset >= map->offset && (offset - map->offset) < (map->end - map->begin)) {
123 HLOGMMM("found fileoffset 0x%" PRIx64 " in map (0x%" PRIx64 " - 0x%" PRIx64
124 " pageoffset 0x%" PRIx64 ") from %s",
125 offset, map->begin, map->end, map->offset, map->name.c_str());
126 return map;
127 }
128 }
129 HLOGM("NOT found offset 0x%" PRIx64 " in maps %zu ", offset, maps_->size());
130 return nullptr;
131 }
132
FindSymbolsFileByMap(std::shared_ptr<DfxMap> inMap) const133 SymbolsFile *VirtualThread::FindSymbolsFileByMap(std::shared_ptr<DfxMap> inMap) const
134 {
135 auto search = symbolsFiles_.find(inMap->name);
136 if (search != symbolsFiles_.end()) {
137 auto& symbolsFile = search->second;
138 HLOGM("found symbol for map '%s'", inMap->name.c_str());
139 symbolsFile->LoadDebugInfo(inMap);
140 return symbolsFile.get();
141 }
142 #ifdef DEBUG_MISS_SYMBOL
143 if (find(missedSymbolFile_.begin(), missedSymbolFile_.end(), inMap->name) ==
144 missedSymbolFile_.end()) {
145 missedSymbolFile_.emplace_back(inMap->name);
146 HLOGW("NOT found symbol for map '%s'", inMap->name.c_str());
147 for (auto &file : symbolsFiles_) {
148 HLOGW(" we have '%s'", file->filePath_.c_str());
149 }
150 }
151 #endif
152 return nullptr;
153 }
154
FindSymbolsFileByName(const std::string & name) const155 SymbolsFile *VirtualThread::FindSymbolsFileByName(const std::string &name) const
156 {
157 auto search = symbolsFiles_.find(name);
158 if (search != symbolsFiles_.end()) {
159 auto& symbolsFile = search->second;
160 HLOGM("found symbol for map '%s'", name.c_str());
161 symbolsFile->LoadDebugInfo();
162 return symbolsFile.get();
163 }
164 #ifdef DEBUG_MISS_SYMBOL
165 if (find(missedSymbolFile_.begin(), missedSymbolFile_.end(), name) ==
166 missedSymbolFile_.end()) {
167 missedSymbolFile_.emplace_back(name);
168 HLOGW("NOT found symbol for map '%s'", name.c_str());
169 for (auto &file : symbolsFiles_) {
170 HLOGW(" we have '%s'", file->filePath_.c_str());
171 }
172 }
173 #endif
174 return nullptr;
175 }
176
ReportVaddrMapMiss(uint64_t vaddr) const177 void VirtualThread::ReportVaddrMapMiss(uint64_t vaddr) const
178 {
179 #ifdef HIPERF_DEBUG
180 if (DebugLogger::GetInstance()->GetLogLevel() <= LEVEL_VERBOSE) {
181 if (missedRuntimeVaddr_.find(vaddr) == missedRuntimeVaddr_.end()) {
182 missedRuntimeVaddr_.insert(vaddr);
183 HLOGV("vaddr %" PRIx64 " not found in any map", vaddr);
184 for (auto &map : *maps_) {
185 HLOGV("map %s ", map->ToString().c_str());
186 }
187 }
188 }
189 #endif
190 }
191
ReadRoMemory(uint64_t vaddr,uint8_t * data,size_t size) const192 bool VirtualThread::ReadRoMemory(uint64_t vaddr, uint8_t *data, size_t size) const
193 {
194 auto [curMemMaps, itemIndex] = virtualruntime_->FindMap(vaddr);
195 if (curMemMaps != nullptr) {
196 // found symbols by file name
197 SymbolsFile *symbolsFile = FindSymbolsFileByMap((curMemMaps->GetMaps())[itemIndex]);
198 if (symbolsFile != nullptr) {
199 std::shared_ptr<DfxMap> map = (curMemMaps->GetMaps())[itemIndex];
200 HLOGM("read vaddr from addr is 0x%" PRIx64 " mapStart :0x%" PRIx64 " mapOffset :0x%" PRIx64 " at '%s'",
201 vaddr - map->begin, map->begin, map->offset, map->name.c_str());
202 map->elf = symbolsFile->GetElfFile();
203 if (map->elf != nullptr) {
204 auto fileOffset = map->FileOffsetFromAddr(vaddr);
205 fileOffset -= map->elf->GetBaseOffset();
206 map->elf->Read(fileOffset, data, size);
207 return true;
208 }
209 HLOGE("ElfFile(%s) is null or read file offset from addr fail", curMemMaps->name_.c_str());
210 return false;
211 } else {
212 HLOGE("found addr %" PRIx64 " in map but not loaded symbole %s", vaddr, curMemMaps->name_.c_str());
213 }
214 } else {
215 #ifdef HIPERF_DEBUG
216 ReportVaddrMapMiss(vaddr);
217 #endif
218 }
219 return false;
220 }
221
ParseMap(std::vector<std::shared_ptr<DfxMap>> & memMaps,bool update)222 bool VirtualThread::ParseMap(std::vector<std::shared_ptr<DfxMap>>& memMaps, bool update)
223 {
224 std::string mapPath = StringPrintf("/proc/%d/maps", pid_);
225 std::shared_ptr<DfxMaps> dfxMaps = OHOS::HiviewDFX::DfxMaps::Create(pid_, mapPath);
226 if (dfxMaps == nullptr) {
227 HLOGE("VirtualThread Failed to Parse Map.");
228 return false;
229 }
230 memMaps = dfxMaps->GetMaps();
231 bool mapsAdded = !update;
232 std::vector<std::shared_ptr<DfxMap>> tempMap;
233 std::string tempMapName;
234 std::shared_ptr<DfxMap> prevMap = nullptr;
235 for (auto memMapItem : memMaps) {
236 if (!update) {
237 virtualruntime_->FillMapsCache(tempMapName, memMapItem);
238 bool updateNormalSymbol = true;
239 if (memMapItem->name.find(".hap") != std::string::npos && (memMapItem->prots & PROT_EXEC)) {
240 memMapItem->prevMap = prevMap;
241 HLOGD("update hap(%s) symbols", memMapItem->name.c_str());
242 updateNormalSymbol = !virtualruntime_->UpdateHapSymbols(memMapItem);
243 }
244 if (updateNormalSymbol) {
245 virtualruntime_->UpdateSymbols(memMapItem->name);
246 }
247 prevMap = memMapItem;
248 } else if (!virtualruntime_->IsSymbolExist(memMapItem->name)) {
249 virtualruntime_->FillMapsCache(tempMapName, memMapItem);
250 mapsAdded = true;
251 tempMap.push_back(memMapItem);
252 bool updateNormalSymbol = true;
253 if (memMapItem->name.find(".hap") != std::string::npos && (memMapItem->prots & PROT_EXEC)) {
254 memMapItem->prevMap = prevMap;
255 HLOGD("update hap(%s) symbols", memMapItem->name.c_str());
256 updateNormalSymbol = !virtualruntime_->UpdateHapSymbols(memMapItem);
257 }
258 if (updateNormalSymbol) {
259 virtualruntime_->UpdateSymbols(memMapItem->name);
260 }
261 prevMap = memMapItem;
262 }
263 }
264
265 // Find if there are duplicate mapping intervals, and if there are, overwrite the old data with the new data.
266 for (auto tempMapIter = tempMap.begin(); tempMapIter != tempMap.end(); ++tempMapIter) {
267 auto memMapIter = std::find_if(memMaps.begin(), memMaps.end(), [&](const std::shared_ptr<DfxMap>& map) {
268 if ((*tempMapIter)->begin == map->begin && (*tempMapIter)->end == map->end) {
269 return true;
270 }
271 return false;
272 });
273 if (memMapIter != memMaps.end()) {
274 virtualruntime_->DelSymbolFile((*memMapIter)->name);
275 memMaps.erase(memMapIter);
276 }
277 }
278 memMaps.insert(memMaps.end(), tempMap.begin(), tempMap.end());
279
280 if (mapsAdded) {
281 PROFILER_LOG_DEBUG(LOG_CORE, "maps changed and need sort");
282 SortMaps();
283 } else {
284 PROFILER_LOG_DEBUG(LOG_CORE, "maps no change");
285 return false;
286 }
287 virtualruntime_->soBegin_ = 0;
288 return true;
289 }
290
SortMaps()291 void VirtualThread::SortMaps()
292 {
293 for (size_t currPos = 1; currPos < maps_->size(); ++currPos) {
294 int targetPos = static_cast<int>(currPos - 1);
295 while (targetPos >= 0 && (*maps_)[currPos]->end < (*maps_)[targetPos]->end) {
296 --targetPos;
297 }
298 if (targetPos < static_cast<int>(currPos - 1)) {
299 auto target = (*maps_)[currPos];
300 for (size_t k = currPos - 1; k > static_cast<size_t>(targetPos); --k) {
301 (*maps_)[k + 1] = (*maps_)[k];
302 }
303 (*maps_)[targetPos + 1] = target;
304 }
305 }
306 return;
307 }
308
CreateMapItem(const std::string filename,uint64_t begin,uint64_t len,uint64_t offset)309 void VirtualThread::CreateMapItem(const std::string filename, uint64_t begin, uint64_t len,
310 uint64_t offset)
311 {
312 if (!OHOS::HiviewDFX::DfxMaps::IsLegalMapItem(filename)) {
313 return; // skip some memmap
314 }
315 uint32_t prots = PROT_EXEC;
316
317 std::shared_ptr<DfxMap> map = std::make_shared<DfxMap>(begin, begin + len, offset, prots, filename);
318 maps_->emplace_back(map);
319 std::string tempMapName{" "};
320 virtualruntime_->FillMapsCache(tempMapName, map);
321 SortMaps();
322 }
323 } // namespace NativeDaemon
324 } // namespace Developtools
325 } // namespace OHOS