1 /*
2 * Copyright (c) 2021-2022 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 #define HILOG_TAG "PerfRecord"
16
17 #include "perf_event_record.h"
18 #include <cinttypes>
19
20 #include "utilities.h"
21
22 using namespace OHOS::HiviewDFX;
23 using namespace std;
24 namespace OHOS {
25 namespace Developtools {
26 namespace HiPerf {
27
28 void *g_sampleMemCache = nullptr; // for read record from buf thread
29 void *g_sampleMemCacheMain = nullptr; // for main thread:collecttionsymbol
30 constexpr size_t SAMPLE_CACHE_SIZE = 4 * 1024;
31
GetPerfEventRecord(const int type,uint8_t * p,const perf_event_attr & attr)32 std::unique_ptr<PerfEventRecord> GetPerfEventRecord(const int type, uint8_t *p,
33 const perf_event_attr &attr)
34 {
35 HLOG_ASSERT(p);
36 uint8_t *data = p;
37
38 // check kernel
39 switch (type) {
40 case PERF_RECORD_SAMPLE:
41 return std::make_unique<PerfRecordSample>(data, attr);
42 case PERF_RECORD_MMAP:
43 return std::make_unique<PerfRecordMmap>(data);
44 case PERF_RECORD_MMAP2:
45 return std::make_unique<PerfRecordMmap2>(data);
46 case PERF_RECORD_LOST:
47 return std::make_unique<PerfRecordLost>(data);
48 case PERF_RECORD_COMM:
49 return std::make_unique<PerfRecordComm>(data);
50 case PERF_RECORD_EXIT:
51 return std::make_unique<PerfRecordExit>(data);
52 case PERF_RECORD_THROTTLE:
53 return std::make_unique<PerfRecordThrottle>(data);
54 case PERF_RECORD_UNTHROTTLE:
55 return std::make_unique<PerfRecordUnthrottle>(data);
56 case PERF_RECORD_FORK:
57 return std::make_unique<PerfRecordFork>(data);
58 case PERF_RECORD_READ:
59 return std::make_unique<PerfRecordRead>(data);
60 case PERF_RECORD_AUX:
61 return std::make_unique<PerfRecordAux>(data);
62 case PERF_RECORD_ITRACE_START:
63 return std::make_unique<PerfRecordItraceStart>(data);
64 case PERF_RECORD_LOST_SAMPLES:
65 return std::make_unique<PerfRecordLostSamples>(data);
66 case PERF_RECORD_SWITCH:
67 return std::make_unique<PerfRecordSwitch>(data);
68 case PERF_RECORD_SWITCH_CPU_WIDE:
69 return std::make_unique<PerfRecordSwitchCpuWide>(data);
70 default:
71 HLOGE("unknown record type %d\n", type);
72 return nullptr;
73 }
74 }
75
GetPerfSampleFromCache(const int type,uint8_t * p,const perf_event_attr & attr)76 std::unique_ptr<PerfEventRecord> GetPerfSampleFromCache(const int type, uint8_t *p,
77 const perf_event_attr &attr)
78 {
79 HLOG_ASSERT(p);
80 uint8_t *data = p;
81
82 if (type == PERF_RECORD_SAMPLE) {
83 if (g_sampleMemCache != nullptr) {
84 memset_s(g_sampleMemCache, SAMPLE_CACHE_SIZE, 0, SAMPLE_CACHE_SIZE);
85 return std::unique_ptr<PerfEventRecord>(new (g_sampleMemCache) PerfRecordSample(data, attr));
86 } else {
87 g_sampleMemCache = std::malloc(SAMPLE_CACHE_SIZE);
88 memset_s(g_sampleMemCache, SAMPLE_CACHE_SIZE, 0, SAMPLE_CACHE_SIZE);
89 return std::unique_ptr<PerfEventRecord>(new (g_sampleMemCache) PerfRecordSample(data, attr));
90 }
91 }
92 return GetPerfEventRecord(type, p, attr);
93 }
94
GetPerfSampleFromCacheMain(const int type,uint8_t * p,const perf_event_attr & attr)95 std::unique_ptr<PerfEventRecord> GetPerfSampleFromCacheMain(const int type, uint8_t *p,
96 const perf_event_attr &attr)
97 {
98 HLOG_ASSERT(p);
99 uint8_t *data = p;
100
101 if (type == PERF_RECORD_SAMPLE) {
102 if (g_sampleMemCacheMain != nullptr) {
103 memset_s(g_sampleMemCacheMain, SAMPLE_CACHE_SIZE, 0, SAMPLE_CACHE_SIZE);
104 return std::unique_ptr<PerfEventRecord>(new (g_sampleMemCacheMain) PerfRecordSample(data, attr));
105 } else {
106 g_sampleMemCacheMain = std::malloc(SAMPLE_CACHE_SIZE);
107 memset_s(g_sampleMemCacheMain, SAMPLE_CACHE_SIZE, 0, SAMPLE_CACHE_SIZE);
108 return std::unique_ptr<PerfEventRecord>(new (g_sampleMemCacheMain) PerfRecordSample(data, attr));
109 }
110 }
111 return GetPerfEventRecord(type, p, attr);
112 }
113
114 template<typename T>
PushToBinary(bool condition,uint8_t * & p,const T & v)115 inline void PushToBinary(bool condition, uint8_t *&p, const T &v)
116 {
117 if (condition) {
118 *(reinterpret_cast<T *>(p)) = v;
119 p += sizeof(T);
120 }
121 }
122
123 template<typename T1, typename T2>
PushToBinary2(bool condition,uint8_t * & p,const T1 & v1,const T2 & v2)124 inline void PushToBinary2(bool condition, uint8_t *&p, const T1 &v1, const T2 &v2)
125 {
126 if (condition) {
127 *(reinterpret_cast<T1 *>(p)) = v1;
128 p += sizeof(T1);
129 *(reinterpret_cast<T2 *>(p)) = v2;
130 p += sizeof(T2);
131 }
132 }
133
134 template<typename T>
PopFromBinary(bool condition,uint8_t * & p,T & v)135 inline void PopFromBinary(bool condition, uint8_t *&p, T &v)
136 {
137 if (condition) {
138 v = *(reinterpret_cast<const T *>(p));
139 p += sizeof(T);
140 }
141 }
142
143 template<typename T1, typename T2>
PopFromBinary2(bool condition,uint8_t * & p,T1 & v1,T2 & v2)144 inline void PopFromBinary2(bool condition, uint8_t *&p, T1 &v1, T2 &v2)
145 {
146 if (condition) {
147 v1 = *(reinterpret_cast<const T1 *>(p));
148 p += sizeof(T1);
149 v2 = *(reinterpret_cast<const T2 *>(p));
150 p += sizeof(T2);
151 }
152 }
153
154 // PerfEventRecord
PerfEventRecord(perf_event_type type,bool in_kernel,const std::string & name)155 PerfEventRecord::PerfEventRecord(perf_event_type type, bool in_kernel, const std::string &name)
156 : name_(name)
157 {
158 header.type = type;
159 header.misc = in_kernel ? PERF_RECORD_MISC_KERNEL : PERF_RECORD_MISC_USER;
160 header.size = sizeof(header);
161 }
162
PerfEventRecord(perf_event_hiperf_ext_type type,const std::string & name)163 PerfEventRecord::PerfEventRecord(perf_event_hiperf_ext_type type, const std::string &name)
164 : name_(name)
165 {
166 header.type = type;
167 header.misc = PERF_RECORD_MISC_USER;
168 header.size = sizeof(header);
169 }
170
PerfEventRecord(uint8_t * p,const std::string & name)171 PerfEventRecord::PerfEventRecord(uint8_t *p, const std::string &name) : name_(name)
172 {
173 header = *(reinterpret_cast<perf_event_header *>(p));
174 }
175
GetHeaderBinary(std::vector<uint8_t> & buf) const176 void PerfEventRecord::GetHeaderBinary(std::vector<uint8_t> &buf) const
177 {
178 if (buf.size() < GetHeaderSize()) {
179 buf.resize(GetHeaderSize());
180 }
181 uint8_t *p = buf.data();
182 *(reinterpret_cast<perf_event_header *>(p)) = header;
183 }
184
Dump(int indent,std::string outputFilename,FILE * outputDump) const185 void PerfEventRecord::Dump(int indent, std::string outputFilename, FILE *outputDump) const
186 {
187 if (outputDump != nullptr) {
188 outputDump_ = outputDump;
189 } else if (!outputFilename.empty() && outputDump_ == nullptr) {
190 std::string resolvedPath = CanonicalizeSpecPath(outputFilename.c_str());
191 outputDump_ = fopen(resolvedPath.c_str(), "w");
192 if (outputDump_ == nullptr) {
193 printf("unable open file to '%s' because '%d'\n", outputFilename.c_str(), errno);
194 return;
195 }
196 }
197 PrintIndent(indent, "\n");
198 PrintIndent(indent, "record %s: type %u, misc %u, size %zu\n", GetName().c_str(), GetType(),
199 GetMisc(), GetSize());
200 DumpData(indent + 1);
201 }
202
DumpLog(const std::string & prefix) const203 void PerfEventRecord::DumpLog(const std::string &prefix) const
204 {
205 HLOGV("%s: record %s: type %u, misc %u, size %zu\n", prefix.c_str(), GetName().c_str(),
206 GetType(), GetMisc(), GetSize());
207 }
208
209 std::vector<u64> PerfRecordSample::ips_ = {};
210 std::vector<CallFrame> PerfRecordSample::callFrames_ = {};
211 std::vector<pid_t> PerfRecordSample::serverPidMap_ = {};
212
DumpLog(const std::string & prefix) const213 void PerfRecordSample::DumpLog(const std::string &prefix) const
214 {
215 HLOGV("%s: SAMPLE: id= %llu size %d pid %u tid %u ips %llu regs %llu, stacks %llu time %llu",
216 prefix.c_str(), data_.sample_id, header.size, data_.pid, data_.tid, data_.nr,
217 data_.reg_nr, data_.dyn_size, data_.time);
218 }
219
RecoverCallStack()220 void PerfRecordSample::RecoverCallStack()
221 {
222 data_.ips = ips_.data();
223 data_.nr = ips_.size();
224 removeStack_ = true;
225 }
226
ReplaceWithCallStack(size_t originalSize)227 void PerfRecordSample::ReplaceWithCallStack(size_t originalSize)
228 {
229 // first we check if we have some user unwind stack need to merge ?
230 if (callFrames_.size() != 0) {
231 // when we have some kernel ips , we cp it first
232 // new size is user call frames + kernel call frames
233 // + PERF_CONTEXT_USER(last + 1) + expand mark(also PERF_CONTEXT_USER)
234 const unsigned int perfContextSize = 2;
235 ips_.reserve(data_.nr + callFrames_.size() + perfContextSize);
236 if (data_.nr > 0) {
237 ips_.assign(data_.ips, data_.ips + data_.nr);
238 }
239 // add user context mark
240 ips_.emplace_back(PERF_CONTEXT_USER);
241 // we also need make a expand mark just for debug only
242 const size_t beginIpsSize = ips_.size();
243 bool ret = std::all_of(callFrames_.begin(), callFrames_.end(), [&](const CallFrame &frame) {
244 ips_.emplace_back(frame.ip_);
245 if (originalSize != 0 and (originalSize != callFrames_.size()) and
246 ips_.size() == (originalSize + beginIpsSize)) {
247 // just for debug
248 // so we can see which frame begin is expand call frames
249 ips_.emplace_back(PERF_CONTEXT_USER);
250 }
251 return true;
252 });
253 if (ret) {
254 HLOGV("combed %zu", callFrames_.size());
255 } else {
256 HLOGV("failed to combed %zu", callFrames_.size());
257 }
258
259 if (sampleType_ & PERF_SAMPLE_REGS_USER) {
260 header.size -= data_.reg_nr * sizeof(u64);
261 data_.reg_nr = 0;
262 data_.user_abi = 0;
263 }
264
265 if (sampleType_ & PERF_SAMPLE_STACK_USER) {
266 // 1. remove the user stack
267 header.size -= data_.stack_size;
268 header.size -= sizeof(data_.dyn_size);
269
270 // 2. clean the size
271 data_.stack_size = 0;
272 data_.dyn_size = 0;
273 }
274
275 if (sampleType_ & PERF_SAMPLE_CALLCHAIN) {
276 HLOGV("ips change from %llu -> %zu", data_.nr, ips_.size());
277
278 // 3. remove the nr size
279 header.size -= data_.nr * sizeof(u64);
280
281 // 4. add new nr size
282 data_.nr = ips_.size();
283 header.size += data_.nr * sizeof(u64);
284
285 // 5. change ips potin to our ips array and hold it.
286 data_.ips = ips_.data();
287 }
288 } else {
289 // nothing need change
290 return;
291 }
292 }
293
PerfRecordSample(uint8_t * p,const perf_event_attr & attr)294 PerfRecordSample::PerfRecordSample(uint8_t *p, const perf_event_attr &attr)
295 : PerfEventRecord(p, "sample")
296 {
297 if (p == nullptr) {
298 HLOG_ASSERT(p);
299 return;
300 }
301 // clear the static vector data
302 Clean();
303 sampleType_ = attr.sample_type;
304
305 uint8_t *start = p;
306
307 p += sizeof(header);
308
309 // parse record according SAMPLE_TYPE
310 PopFromBinary(sampleType_ & PERF_SAMPLE_IDENTIFIER, p, data_.sample_id);
311 PopFromBinary(sampleType_ & PERF_SAMPLE_IP, p, data_.ip);
312 PopFromBinary2(sampleType_ & PERF_SAMPLE_TID, p, data_.pid, data_.tid);
313 PopFromBinary(sampleType_ & PERF_SAMPLE_TIME, p, data_.time);
314 PopFromBinary(sampleType_ & PERF_SAMPLE_ADDR, p, data_.addr);
315 PopFromBinary(sampleType_ & PERF_SAMPLE_ID, p, data_.id);
316 PopFromBinary(sampleType_ & PERF_SAMPLE_STREAM_ID, p, data_.stream_id);
317 PopFromBinary2(sampleType_ & PERF_SAMPLE_CPU, p, data_.cpu, data_.res);
318 PopFromBinary(sampleType_ & PERF_SAMPLE_PERIOD, p, data_.period);
319 PopFromBinary(sampleType_ & PERF_SAMPLE_CALLCHAIN, p, data_.nr);
320 if (data_.nr > 0) {
321 // the pointer is from input(p), require caller keep input(p) with *this together
322 // think it in next time
323 data_.ips = reinterpret_cast<u64 *>(p);
324 p += data_.nr * sizeof(u64);
325 }
326 PopFromBinary(sampleType_ & PERF_SAMPLE_RAW, p, data_.raw_size);
327 if (data_.raw_size > 0) {
328 data_.raw_data = p;
329 p += data_.raw_size * sizeof(u8);
330 }
331 PopFromBinary(sampleType_ & PERF_SAMPLE_BRANCH_STACK, p, data_.bnr);
332 if (data_.bnr > 0) {
333 data_.lbr = reinterpret_cast<perf_branch_entry *>(p);
334 p += data_.bnr * sizeof(perf_branch_entry);
335 }
336 PopFromBinary(sampleType_ & PERF_SAMPLE_REGS_USER, p, data_.user_abi);
337 if (data_.user_abi > 0) {
338 data_.reg_mask = attr.sample_regs_user;
339 data_.reg_nr = __builtin_popcountll(data_.reg_mask);
340 data_.user_regs = reinterpret_cast<u64 *>(p);
341 p += data_.reg_nr * sizeof(u64);
342 }
343 PopFromBinary(sampleType_ & PERF_SAMPLE_SERVER_PID, p, data_.server_nr);
344 if (data_.server_nr > 0) {
345 data_.server_pids = reinterpret_cast<u64 *>(p);
346 p += data_.server_nr * sizeof(u64);
347 }
348 PopFromBinary(sampleType_ & PERF_SAMPLE_STACK_USER, p, data_.stack_size);
349 if (data_.stack_size > 0) {
350 data_.stack_data = p;
351 p += data_.stack_size;
352 PopFromBinary(true, p, data_.dyn_size);
353 }
354 uint32_t remain = header.size - (p - start);
355 if (data_.nr == 0 && dumpRemoveStack_ && remain == sizeof(stackId_)) {
356 PopFromBinary(true, p, stackId_.value);
357 }
358 }
359
GetBinary(std::vector<uint8_t> & buf) const360 bool PerfRecordSample::GetBinary(std::vector<uint8_t> &buf) const
361 {
362 if (buf.size() < GetSize()) {
363 buf.resize(GetSize());
364 }
365
366 GetHeaderBinary(buf);
367 uint8_t *p = buf.data() + GetHeaderSize();
368
369 PushToBinary(sampleType_ & PERF_SAMPLE_IDENTIFIER, p, data_.sample_id);
370 PushToBinary(sampleType_ & PERF_SAMPLE_IP, p, data_.ip);
371 PushToBinary2(sampleType_ & PERF_SAMPLE_TID, p, data_.pid, data_.tid);
372 PushToBinary(sampleType_ & PERF_SAMPLE_TIME, p, data_.time);
373 PushToBinary(sampleType_ & PERF_SAMPLE_ADDR, p, data_.addr);
374 PushToBinary(sampleType_ & PERF_SAMPLE_ID, p, data_.id);
375 PushToBinary(sampleType_ & PERF_SAMPLE_STREAM_ID, p, data_.stream_id);
376 PushToBinary2(sampleType_ & PERF_SAMPLE_CPU, p, data_.cpu, data_.res);
377 PushToBinary(sampleType_ & PERF_SAMPLE_PERIOD, p, data_.period);
378 PushToBinary(sampleType_ & PERF_SAMPLE_CALLCHAIN, p, data_.nr);
379 if (data_.nr > 0 && !removeStack_) {
380 std::copy(data_.ips + skipKernel_, data_.ips + data_.nr + skipKernel_,
381 reinterpret_cast<u64 *>(p));
382 p += data_.nr * sizeof(u64);
383 }
384 PushToBinary(sampleType_ & PERF_SAMPLE_RAW, p, data_.raw_size);
385 if (data_.raw_size > 0) {
386 std::copy(data_.raw_data, data_.raw_data + data_.raw_size, p);
387 p += data_.raw_size * sizeof(u8);
388 }
389 PushToBinary(sampleType_ & PERF_SAMPLE_BRANCH_STACK, p, data_.bnr);
390 if (data_.bnr > 0) {
391 std::copy(data_.lbr, data_.lbr + data_.bnr, reinterpret_cast<perf_branch_entry *>(p));
392 p += data_.bnr * sizeof(perf_branch_entry);
393 }
394 PushToBinary(sampleType_ & PERF_SAMPLE_REGS_USER, p, data_.user_abi);
395 if (data_.user_abi > 0 && data_.reg_nr > 0) {
396 std::copy(data_.user_regs, data_.user_regs + data_.reg_nr, reinterpret_cast<u64 *>(p));
397 p += data_.reg_nr * sizeof(u64);
398 }
399 PushToBinary(sampleType_ & PERF_SAMPLE_SERVER_PID, p, data_.server_nr);
400 if (data_.server_nr > 0) {
401 std::copy(data_.server_pids + skipPid_, data_.server_pids + data_.server_nr + skipPid_,
402 reinterpret_cast<u64 *>(p));
403 p += data_.server_nr * sizeof(u64);
404 }
405 PushToBinary(sampleType_ & PERF_SAMPLE_STACK_USER, p, data_.stack_size);
406 if (data_.stack_size > 0) {
407 std::copy(data_.stack_data, data_.stack_data + data_.stack_size, p);
408 p += data_.stack_size * sizeof(u8);
409 PushToBinary(true, p, data_.dyn_size);
410 }
411 PushToBinary(removeStack_, p, stackId_.value);
412 return true;
413 }
414
DumpData(int indent) const415 void PerfRecordSample::DumpData(int indent) const
416 {
417 PrintIndent(indent, "sample_type: 0x%" PRIx64 "\n", sampleType_);
418
419 // dump record according sampleType
420 if (sampleType_ & (PERF_SAMPLE_ID | PERF_SAMPLE_IDENTIFIER)) {
421 PrintIndent(indent, "ID %" PRIu64 "\n", static_cast<uint64_t>(data_.sample_id));
422 }
423 if (sampleType_ & PERF_SAMPLE_IP) {
424 PrintIndent(indent, "ip %llx\n", data_.ip);
425 }
426 if (sampleType_ & PERF_SAMPLE_TID) {
427 PrintIndent(indent, "pid %u, tid %u\n", data_.pid, data_.tid);
428 }
429 if (sampleType_ & PERF_SAMPLE_TIME) {
430 PrintIndent(indent, "time %llu\n", data_.time);
431 }
432 if (sampleType_ & PERF_SAMPLE_ADDR) {
433 PrintIndent(indent, "addr %p\n", reinterpret_cast<void *>(data_.addr));
434 }
435 if (sampleType_ & PERF_SAMPLE_STREAM_ID) {
436 PrintIndent(indent, "stream_id %" PRIu64 "\n", static_cast<uint64_t>(data_.stream_id));
437 }
438 if (sampleType_ & PERF_SAMPLE_CPU) {
439 PrintIndent(indent, "cpu %u, res %u\n", data_.cpu, data_.res);
440 }
441 if (sampleType_ & PERF_SAMPLE_PERIOD) {
442 PrintIndent(indent, "period %" PRIu64 "\n", static_cast<uint64_t>(data_.period));
443 }
444 if (stackId_.section.id > 0) {
445 PrintIndent(indent, "stackid %" PRIu64 "\n", static_cast<uint64_t>(stackId_.section.id));
446 }
447 if (sampleType_ & PERF_SAMPLE_CALLCHAIN) {
448 bool userContext = false;
449 PrintIndent(indent, "callchain nr=%lld\n", data_.nr);
450 for (uint64_t i = 0; i < data_.nr; ++i) {
451 std::string_view supplement = "";
452 if ((sampleType_ & PERF_SAMPLE_STACK_USER) == 0 || data_.ips[i] != PERF_CONTEXT_USER) {
453 PrintIndent(indent + 1, "0x%llx%s\n", data_.ips[i], supplement.data());
454 continue;
455 }
456 // is PERF_SAMPLE_STACK_USER type and is PERF_CONTEXT_USER
457 if (!userContext) {
458 userContext = true;
459 supplement = " <unwind callstack>";
460 } else {
461 supplement = " <expand callstack>";
462 }
463 PrintIndent(indent + 1, "0x%llx%s\n", data_.ips[i], supplement.data());
464 }
465 }
466 if (sampleType_ & PERF_SAMPLE_RAW) {
467 PrintIndent(indent, "raw size=%u\n", data_.raw_size);
468 const uint32_t *data = reinterpret_cast<const uint32_t *>(data_.raw_data);
469 size_t size = data_.raw_size / sizeof(uint32_t);
470 for (size_t i = 0; i < size; ++i) {
471 PrintIndent(indent + 1, "0x%08x (%x)\n", data[i], data[i]);
472 }
473 }
474 if (sampleType_ & PERF_SAMPLE_BRANCH_STACK) {
475 PrintIndent(indent, "branch_stack nr=%lld\n", data_.bnr);
476 for (uint64_t i = 0; i < data_.bnr; ++i) {
477 auto &item = data_.lbr[i];
478 PrintIndent(indent + 1, "from 0x%llx, to 0x%llx %s%s\n", item.from, item.to,
479 item.mispred ? "mispred" : "", item.predicted ? "predicted" : "");
480 }
481 }
482 if (sampleType_ & PERF_SAMPLE_REGS_USER) {
483 PrintIndent(indent, "user regs: abi=%lld, reg_nr=%lld\n", data_.user_abi, data_.reg_nr);
484 for (uint64_t i = 0; i < data_.reg_nr; ++i) {
485 PrintIndent(indent + 1, "0x%llx\n", data_.user_regs[i]);
486 }
487 }
488 if (sampleType_ & PERF_SAMPLE_SERVER_PID) {
489 PrintIndent(indent, "server nr=%lld\n", data_.server_nr);
490 for (uint64_t i = 0; i < data_.server_nr; ++i) {
491 PrintIndent(indent + 1, "pid: %llu\n", data_.server_pids[i]);
492 }
493 }
494 if (sampleType_ & PERF_SAMPLE_STACK_USER) {
495 PrintIndent(indent, "user stack: size %llu dyn_size %lld\n", data_.stack_size,
496 data_.dyn_size);
497 }
498 }
499
GetPid() const500 inline pid_t PerfRecordSample::GetPid() const
501 {
502 return data_.pid;
503 }
504
Clean()505 void PerfRecordSample::Clean()
506 {
507 ips_.clear();
508 callFrames_.clear();
509 serverPidMap_.clear();
510 }
511
PerfRecordMmap(uint8_t * p)512 PerfRecordMmap::PerfRecordMmap(uint8_t *p) : PerfEventRecord(p, "mmap")
513 {
514 size_t copySize = GetSize() - sizeof(header);
515 if (memcpy_s((uint8_t *)&data_, sizeof(data_), p + sizeof(header), copySize) != 0) {
516 HLOGE("memcpy_s retren failed !!!");
517 }
518 }
519
PerfRecordMmap(bool inKernel,u32 pid,u32 tid,u64 addr,u64 len,u64 pgoff,const std::string & filename)520 PerfRecordMmap::PerfRecordMmap(bool inKernel, u32 pid, u32 tid, u64 addr, u64 len, u64 pgoff,
521 const std::string &filename)
522 : PerfEventRecord(PERF_RECORD_MMAP, inKernel, "mmap")
523 {
524 data_.pid = pid;
525 data_.tid = tid;
526 data_.addr = addr;
527 data_.len = len;
528 data_.pgoff = pgoff;
529 if (strncpy_s(data_.filename, KILO, filename.c_str(), filename.size()) != 0) {
530 HLOGE("strncpy_s failed");
531 }
532
533 header.size = sizeof(header) + sizeof(data_) - KILO + filename.size() + 1;
534 }
535
GetBinary(std::vector<uint8_t> & buf) const536 bool PerfRecordMmap::GetBinary(std::vector<uint8_t> &buf) const
537 {
538 if (buf.size() < GetSize()) {
539 buf.resize(GetSize());
540 }
541
542 GetHeaderBinary(buf);
543 uint8_t *p = buf.data() + GetHeaderSize();
544
545 // data_.filename[] is variable-length
546 std::copy((uint8_t *)&data_, (uint8_t *)&data_ + GetSize() - GetHeaderSize(), p);
547 return true;
548 }
549
DumpData(int indent) const550 void PerfRecordMmap::DumpData(int indent) const
551 {
552 PrintIndent(indent, "pid %u, tid %u, addr 0x%llx, len 0x%llx\n", data_.pid, data_.tid,
553 data_.addr, data_.len);
554 PrintIndent(indent, "pgoff 0x%llx, filename %s\n", data_.pgoff, data_.filename);
555 }
556
DumpLog(const std::string & prefix) const557 void PerfRecordMmap::DumpLog(const std::string &prefix) const
558 {
559 HLOGV("%s: MMAP: size %d pid %u tid %u dso '%s' (0x%llx-0x%llx)@0x%llx", prefix.c_str(),
560 header.size, data_.pid, data_.tid, data_.filename, data_.addr, data_.addr + data_.len,
561 data_.pgoff);
562 }
563
PerfRecordMmap2(uint8_t * p)564 PerfRecordMmap2::PerfRecordMmap2(uint8_t *p) : PerfEventRecord(p, "mmap2")
565 {
566 size_t copySize = GetSize() - sizeof(header);
567 if (memcpy_s((uint8_t *)&data_, sizeof(data_), p + sizeof(header), copySize) != 0) {
568 HLOGE("memcpy_s retren failed !!!");
569 }
570 }
571
PerfRecordMmap2(bool inKernel,u32 pid,u32 tid,u64 addr,u64 len,u64 pgoff,u32 maj,u32 min,u64 ino,u32 prot,u32 flags,const std::string & filename)572 PerfRecordMmap2::PerfRecordMmap2(bool inKernel, u32 pid, u32 tid, u64 addr, u64 len, u64 pgoff,
573 u32 maj, u32 min, u64 ino, u32 prot, u32 flags,
574 const std::string &filename)
575 : PerfEventRecord(PERF_RECORD_MMAP2, inKernel, "mmap2")
576 {
577 data_.pid = pid;
578 data_.tid = tid;
579 data_.addr = addr;
580 data_.len = len;
581 data_.pgoff = pgoff;
582 data_.maj = maj;
583 data_.min = min;
584 data_.ino = ino;
585 data_.ino_generation = 0;
586 data_.prot = prot;
587 data_.flags = flags;
588 if (strncpy_s(data_.filename, KILO, filename.c_str(), filename.size()) != 0) {
589 HLOGE("strncpy_s failed");
590 }
591
592 header.size = sizeof(header) + sizeof(data_) - KILO + filename.size() + 1;
593 }
594
PerfRecordMmap2(bool inKernel,u32 pid,u32 tid,std::shared_ptr<DfxMap> item)595 PerfRecordMmap2::PerfRecordMmap2(bool inKernel, u32 pid, u32 tid, std::shared_ptr<DfxMap> item)
596 : PerfEventRecord(PERF_RECORD_MMAP2, inKernel, "mmap2")
597 {
598 data_.pid = pid;
599 data_.tid = tid;
600 data_.addr = item->begin;
601 data_.len = item->end - item->begin;
602 data_.pgoff = item->offset;
603 data_.maj = item->major;
604 data_.min = item->minor;
605 data_.ino = item->inode;
606 data_.ino_generation = 0;
607 DfxMap::PermsToProts(item->perms, data_.prot, data_.flags);
608 if (strncpy_s(data_.filename, KILO, item->name.c_str(), item->name.size()) != 0) {
609 HLOGE("strncpy_s failed");
610 }
611
612 header.size = sizeof(header) + sizeof(data_) - KILO + item->name.size() + 1;
613 }
614
GetBinary(std::vector<uint8_t> & buf) const615 bool PerfRecordMmap2::GetBinary(std::vector<uint8_t> &buf) const
616 {
617 if (buf.size() < GetSize()) {
618 buf.resize(GetSize());
619 }
620
621 GetHeaderBinary(buf);
622 uint8_t *p = buf.data() + GetHeaderSize();
623
624 // data_.filename[] is variable-length
625 std::copy((uint8_t *)&data_, (uint8_t *)&data_ + GetSize() - GetHeaderSize(), p);
626 return true;
627 }
628
DumpData(int indent) const629 void PerfRecordMmap2::DumpData(int indent) const
630 {
631 PrintIndent(indent, "pid %u, tid %u, addr 0x%llx, len 0x%llx\n", data_.pid, data_.tid,
632 data_.addr, data_.len);
633 PrintIndent(indent, "pgoff 0x%llx, maj %u, min %u, ino %llu, ino_generation %llu\n",
634 data_.pgoff, data_.maj, data_.min, data_.ino, data_.ino_generation);
635 PrintIndent(indent, "prot %u, flags %u, filename %s\n", data_.prot, data_.flags,
636 data_.filename);
637 }
DumpLog(const std::string & prefix) const638 void PerfRecordMmap2::DumpLog(const std::string &prefix) const
639 {
640 HLOGV("%s: MMAP2: size %d pid %u tid %u dso '%s' (0x%llx-0x%llx)@0x%llx", prefix.c_str(),
641 header.size, data_.pid, data_.tid, data_.filename, data_.addr, data_.addr + data_.len,
642 data_.pgoff);
643 }
644
PerfRecordLost(uint8_t * p)645 PerfRecordLost::PerfRecordLost(uint8_t *p) : PerfEventRecord(p, "lost")
646 {
647 size_t copySize = GetSize() - sizeof(header);
648 if (memcpy_s((uint8_t *)&data_, sizeof(data_), p + sizeof(header), copySize) != 0) {
649 HLOGE("memcpy_s retren failed !!!");
650 }
651 }
652
GetBinary(std::vector<uint8_t> & buf) const653 bool PerfRecordLost::GetBinary(std::vector<uint8_t> &buf) const
654 {
655 if (buf.size() < GetSize()) {
656 buf.resize(GetSize());
657 }
658
659 GetHeaderBinary(buf);
660 uint8_t *p = buf.data() + GetHeaderSize();
661
662 auto pDest = reinterpret_cast<PerfRecordLostData *>(p);
663 *pDest = data_;
664
665 return true;
666 }
667
DumpData(int indent) const668 void PerfRecordLost::DumpData(int indent) const
669 {
670 PrintIndent(indent, "id %llu, lost %llu\n", data_.id, data_.lost);
671 }
672
PerfRecordComm(uint8_t * p)673 PerfRecordComm::PerfRecordComm(uint8_t *p) : PerfEventRecord(p, "comm")
674 {
675 size_t copySize = GetSize() - sizeof(header);
676 if (memcpy_s((uint8_t *)&data_, sizeof(data_), p + sizeof(header), copySize) != 0) {
677 HLOGE("memcpy_s retren failed !!!");
678 }
679 }
680
PerfRecordComm(bool inKernel,u32 pid,u32 tid,const std::string & comm)681 PerfRecordComm::PerfRecordComm(bool inKernel, u32 pid, u32 tid, const std::string &comm)
682 : PerfEventRecord(PERF_RECORD_COMM, inKernel, "comm")
683 {
684 data_.pid = pid;
685 data_.tid = tid;
686 if (strncpy_s(data_.comm, KILO, comm.c_str(), comm.size()) != 0) {
687 HLOGE("strncpy_s failed !!!");
688 }
689
690 header.size = sizeof(header) + sizeof(data_) - KILO + comm.size() + 1;
691 }
692
GetBinary(std::vector<uint8_t> & buf) const693 bool PerfRecordComm::GetBinary(std::vector<uint8_t> &buf) const
694 {
695 if (buf.size() < GetSize()) {
696 buf.resize(GetSize());
697 }
698
699 GetHeaderBinary(buf);
700 uint8_t *p = buf.data() + GetHeaderSize();
701
702 // data_.comm[] is variable-length
703 std::copy((uint8_t *)&data_, (uint8_t *)&data_ + GetSize() - GetHeaderSize(), p);
704
705 return true;
706 }
707
DumpData(int indent) const708 void PerfRecordComm::DumpData(int indent) const
709 {
710 PrintIndent(indent, "pid %u, tid %u, comm %s\n", data_.pid, data_.tid, data_.comm);
711 }
712
DumpLog(const std::string & prefix) const713 void PerfRecordComm::DumpLog(const std::string &prefix) const
714 {
715 HLOGV("pid %u, tid %u, comm %s\n", data_.pid, data_.tid, data_.comm);
716 }
717
PerfRecordExit(uint8_t * p)718 PerfRecordExit::PerfRecordExit(uint8_t *p) : PerfEventRecord(p, "exit")
719 {
720 size_t copySize = GetSize() - sizeof(header);
721 if (memcpy_s((uint8_t *)&data_, sizeof(data_), p + sizeof(header), copySize) != 0) {
722 HLOGE("memcpy_s retren failed !!!");
723 }
724 }
725
GetBinary(std::vector<uint8_t> & buf) const726 bool PerfRecordExit::GetBinary(std::vector<uint8_t> &buf) const
727 {
728 if (buf.size() < GetSize()) {
729 buf.resize(GetSize());
730 }
731
732 GetHeaderBinary(buf);
733 uint8_t *p = buf.data() + GetHeaderSize();
734
735 auto pDest = reinterpret_cast<PerfRecordExitData *>(p);
736 *pDest = data_;
737 return true;
738 }
739
DumpData(int indent) const740 void PerfRecordExit::DumpData(int indent) const
741 {
742 PrintIndent(indent, "pid %u, ppid %u, tid %u, ptid %u time 0x%llx\n", data_.pid, data_.ppid,
743 data_.tid, data_.ptid, data_.time);
744 }
745
PerfRecordThrottle(uint8_t * p)746 PerfRecordThrottle::PerfRecordThrottle(uint8_t *p) : PerfEventRecord(p, "throttle")
747 {
748 size_t copySize = GetSize() - sizeof(header);
749 if (memcpy_s((uint8_t *)&data_, sizeof(data_), p + sizeof(header), copySize) != 0) {
750 HLOGE("memcpy_s retren failed !!!");
751 }
752 }
753
GetBinary(std::vector<uint8_t> & buf) const754 bool PerfRecordThrottle::GetBinary(std::vector<uint8_t> &buf) const
755 {
756 if (buf.size() < GetSize()) {
757 buf.resize(GetSize());
758 }
759
760 GetHeaderBinary(buf);
761 uint8_t *p = buf.data() + GetHeaderSize();
762
763 auto pDest = reinterpret_cast<PerfRecordThrottleData *>(p);
764 *pDest = data_;
765 return true;
766 }
767
DumpData(int indent) const768 void PerfRecordThrottle::DumpData(int indent) const
769 {
770 PrintIndent(indent, "time 0x%llx, id %llx, stream_id %llx\n", data_.time, data_.id,
771 data_.stream_id);
772 }
773
PerfRecordUnthrottle(uint8_t * p)774 PerfRecordUnthrottle::PerfRecordUnthrottle(uint8_t *p) : PerfEventRecord(p, "unthrottle")
775 {
776 size_t copySize = GetSize() - sizeof(header);
777 if (memcpy_s((uint8_t *)&data_, sizeof(data_), p + sizeof(header), copySize) != 0) {
778 HLOGE("memcpy_s retren failed !!!");
779 }
780 }
781
GetBinary(std::vector<uint8_t> & buf) const782 bool PerfRecordUnthrottle::GetBinary(std::vector<uint8_t> &buf) const
783 {
784 if (buf.size() < GetSize()) {
785 buf.resize(GetSize());
786 }
787
788 GetHeaderBinary(buf);
789 uint8_t *p = buf.data() + GetHeaderSize();
790
791 auto pDest = reinterpret_cast<PerfRecordThrottleData *>(p);
792 *pDest = data_;
793 return true;
794 }
DumpData(int indent) const795 void PerfRecordUnthrottle::DumpData(int indent) const
796 {
797 PrintIndent(indent, "time 0x%llx, id %llx, stream_id %llx\n", data_.time, data_.id,
798 data_.stream_id);
799 }
800
PerfRecordFork(uint8_t * p)801 PerfRecordFork::PerfRecordFork(uint8_t *p) : PerfEventRecord(p, "fork")
802 {
803 size_t copySize = GetSize() - sizeof(header);
804 if (memcpy_s((uint8_t *)&data_, sizeof(data_), p + sizeof(header), copySize) != 0) {
805 HLOGE("memcpy_s retren failed !!!");
806 }
807 }
808
GetBinary(std::vector<uint8_t> & buf) const809 bool PerfRecordFork::GetBinary(std::vector<uint8_t> &buf) const
810 {
811 if (buf.size() < GetSize()) {
812 buf.resize(GetSize());
813 }
814
815 GetHeaderBinary(buf);
816 uint8_t *p = buf.data() + GetHeaderSize();
817
818 auto pDest = reinterpret_cast<PerfRecordForkData *>(p);
819 *pDest = data_;
820 return true;
821 }
822
DumpData(int indent) const823 void PerfRecordFork::DumpData(int indent) const
824 {
825 PrintIndent(indent, "pid %u, ppid %u, tid %u, ptid %u\n", data_.pid, data_.ppid, data_.tid,
826 data_.ptid);
827 }
828
PerfRecordRead(uint8_t * p)829 PerfRecordRead::PerfRecordRead(uint8_t *p) : PerfEventRecord(p, "read")
830 {
831 size_t copySize = GetSize() - sizeof(header);
832 if (memcpy_s((uint8_t *)&data_, sizeof(data_), p + sizeof(header), copySize) != 0) {
833 HLOGE("memcpy_s retren failed !!!");
834 }
835 }
836
GetBinary(std::vector<uint8_t> & buf) const837 bool PerfRecordRead::GetBinary(std::vector<uint8_t> &buf) const
838 {
839 if (buf.size() < GetSize()) {
840 buf.resize(GetSize());
841 }
842
843 GetHeaderBinary(buf);
844 uint8_t *p = buf.data() + GetHeaderSize();
845
846 auto pDest = reinterpret_cast<PerfRecordReadData *>(p);
847 *pDest = data_;
848 return true;
849 }
850
DumpData(int indent) const851 void PerfRecordRead::DumpData(int indent) const
852 {
853 PrintIndent(indent, "pid %u, tid %u\n", data_.pid, data_.tid);
854 PrintIndent(indent, "values: value %llx, time_enabled %llx, time_running %llx, id %llx\n",
855 data_.values.value, data_.values.time_enabled, data_.values.time_running,
856 data_.values.id);
857 }
858
PerfRecordAux(uint8_t * p)859 PerfRecordAux::PerfRecordAux(uint8_t *p) : PerfEventRecord(p, "aux")
860 {
861 size_t copySize = GetSize() - sizeof(header);
862 if (memcpy_s((uint8_t *)&data_, sizeof(data_), p + sizeof(header), copySize) != 0) {
863 HLOGE("memcpy_s retren failed !!!");
864 }
865 }
866
GetBinary(std::vector<uint8_t> & buf) const867 bool PerfRecordAux::GetBinary(std::vector<uint8_t> &buf) const
868 {
869 if (buf.size() < GetSize()) {
870 buf.resize(GetSize());
871 }
872
873 GetHeaderBinary(buf);
874 uint8_t *p = buf.data() + GetHeaderSize();
875
876 auto pDest = reinterpret_cast<PerfRecordAuxData *>(p);
877 *pDest = data_;
878 return true;
879 }
880
DumpData(int indent) const881 void PerfRecordAux::DumpData(int indent) const
882 {
883 PrintIndent(indent, "aux_offset %llx, aux_size %llx, flags %llx\n", data_.aux_offset,
884 data_.aux_size, data_.flags);
885 }
886
PerfRecordItraceStart(uint8_t * p)887 PerfRecordItraceStart::PerfRecordItraceStart(uint8_t *p) : PerfEventRecord(p, "itraceStart")
888 {
889 size_t copySize = GetSize() - sizeof(header);
890 if (memcpy_s((uint8_t *)&data_, sizeof(data_), p + sizeof(header), copySize) != 0) {
891 HLOGE("memcpy_s retren failed !!!");
892 }
893 }
894
GetBinary(std::vector<uint8_t> & buf) const895 bool PerfRecordItraceStart::GetBinary(std::vector<uint8_t> &buf) const
896 {
897 if (buf.size() < GetSize()) {
898 buf.resize(GetSize());
899 }
900
901 GetHeaderBinary(buf);
902 uint8_t *p = buf.data() + GetHeaderSize();
903
904 auto pDest = reinterpret_cast<PerfRecordItraceStartData *>(p);
905 *pDest = data_;
906 return true;
907 }
908
DumpData(int indent) const909 void PerfRecordItraceStart::DumpData(int indent) const
910 {
911 PrintIndent(indent, "pid %u, tid %u\n", data_.pid, data_.tid);
912 }
913
PerfRecordLostSamples(uint8_t * p)914 PerfRecordLostSamples::PerfRecordLostSamples(uint8_t *p) : PerfEventRecord(p, "lostSamples")
915 {
916 size_t copySize = GetSize() - sizeof(header);
917 if (memcpy_s((uint8_t *)&data_, sizeof(data_), p + sizeof(header), copySize) != 0) {
918 HLOGE("memcpy_s retren failed !!!");
919 }
920 }
921
GetBinary(std::vector<uint8_t> & buf) const922 bool PerfRecordLostSamples::GetBinary(std::vector<uint8_t> &buf) const
923 {
924 if (buf.size() < GetSize()) {
925 buf.resize(GetSize());
926 }
927
928 GetHeaderBinary(buf);
929 uint8_t *p = buf.data() + GetHeaderSize();
930
931 auto pDest = reinterpret_cast<PerfRecordLostSamplesData *>(p);
932 *pDest = data_;
933 return true;
934 }
935
DumpData(int indent) const936 void PerfRecordLostSamples::DumpData(int indent) const
937 {
938 PrintIndent(indent, "lost %llu\n", data_.lost);
939 }
940
PerfRecordSwitch(uint8_t * p)941 PerfRecordSwitch::PerfRecordSwitch(uint8_t *p) : PerfEventRecord(p, "switch")
942 {
943 size_t copySize = GetSize() - sizeof(header);
944 if (memcpy_s((uint8_t *)&data_, sizeof(data_), p + sizeof(header), copySize) != 0) {
945 HLOGE("memcpy_s retren failed !!!");
946 }
947 }
948
GetBinary(std::vector<uint8_t> & buf) const949 bool PerfRecordSwitch::GetBinary(std::vector<uint8_t> &buf) const
950 {
951 if (buf.size() < GetSize()) {
952 buf.resize(GetSize());
953 }
954
955 GetHeaderBinary(buf);
956 uint8_t *p = buf.data() + GetHeaderSize();
957
958 auto pDest = reinterpret_cast<PerfRecordSwitchData *>(p);
959 *pDest = data_;
960 return true;
961 }
962
PerfRecordSwitchCpuWide(uint8_t * p)963 PerfRecordSwitchCpuWide::PerfRecordSwitchCpuWide(uint8_t *p) : PerfEventRecord(p, "switchCpuWide")
964 {
965 size_t copySize = GetSize() - sizeof(header);
966 if (memcpy_s((uint8_t *)&data_, sizeof(data_), p + sizeof(header), copySize) != 0) {
967 HLOGE("memcpy_s retren failed !!!");
968 }
969 }
970
GetBinary(std::vector<uint8_t> & buf) const971 bool PerfRecordSwitchCpuWide::GetBinary(std::vector<uint8_t> &buf) const
972 {
973 if (buf.size() < GetSize()) {
974 buf.resize(GetSize());
975 }
976
977 GetHeaderBinary(buf);
978 uint8_t *p = buf.data() + GetHeaderSize();
979
980 auto pDest = reinterpret_cast<PerfRecordSwitchCpuWideData *>(p);
981 *pDest = data_;
982 return true;
983 }
984
DumpData(int indent) const985 void PerfRecordSwitchCpuWide::DumpData(int indent) const
986 {
987 PrintIndent(indent, "next_prev_pid %u, next_prev_tid %u\n", data_.next_prev_pid,
988 data_.next_prev_tid);
989 }
990
GetUstackServerPid()991 pid_t PerfRecordSample::GetUstackServerPid()
992 {
993 if (!data_.server_nr) {
994 return data_.pid;
995 }
996
997 size_t curr_server = 0;
998 // ip_nr == 1...nr: server_pid of data_.ips[nr]
999 for (size_t i = 1; i < data_.nr; i++) {
1000 // context change, use next server pid
1001 if (data_.ips[i] >= PERF_CONTEXT_MAX) {
1002 curr_server++;
1003 }
1004 }
1005 // ip_nr == nr + 1: server_pid of ustack
1006 if (curr_server > 0) {
1007 curr_server++;
1008 }
1009 if (curr_server >= data_.server_nr) {
1010 HLOGE("ustack server pid nr %zu out of range", curr_server);
1011 return data_.pid;
1012 }
1013
1014 // return server pid
1015 return data_.server_pids[curr_server];
1016 }
1017
GetServerPidof(unsigned int ip_nr)1018 pid_t PerfRecordSample::GetServerPidof(unsigned int ip_nr)
1019 {
1020 if (!data_.server_nr) {
1021 return data_.pid;
1022 }
1023
1024 // init serverPidMap_
1025 if (!serverPidMap_.size()) {
1026 size_t curr_server = 0;
1027 // ip_nr == 0: server_pid of data_.ip
1028 serverPidMap_.emplace_back(data_.server_pids[curr_server]);
1029 // ip_nr == 1...nr: server_pid of data_.ips[nr]
1030 for (size_t i = 1; i < data_.nr; i++) {
1031 // context change, use next server pid
1032 if (data_.ips[i] >= PERF_CONTEXT_MAX) {
1033 curr_server++;
1034 }
1035 if (curr_server >= data_.server_nr) {
1036 HLOGE("callchain server pid nr %zu out of range", curr_server);
1037 break;
1038 }
1039 serverPidMap_.emplace_back(data_.server_pids[curr_server]);
1040 }
1041 }
1042
1043 // return server pid
1044 if (ip_nr >= serverPidMap_.size()) {
1045 return data_.pid;
1046 } else {
1047 return serverPidMap_[ip_nr];
1048 }
1049 }
1050 } // namespace HiPerf
1051 } // namespace Developtools
1052 } // namespace OHOS
1053