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
16 #include "ecmascript/dfx/cpu_profiler/samples_record.h"
17
18 #include <climits>
19 #include <sys/syscall.h>
20 #include <unistd.h>
21
22 #include "ecmascript/dfx/cpu_profiler/cpu_profiler.h"
23 #include "ecmascript/dfx/cpu_profiler/sampling_processor.h"
24 #include "ecmascript/dfx/tracing/tracing.h"
25 #include "ecmascript/ecma_vm.h"
26 #include "ecmascript/interpreter/interpreter.h"
27 #include "ecmascript/method.h"
28
29 namespace panda::ecmascript {
SamplesRecord()30 SamplesRecord::SamplesRecord()
31 {
32 profileInfo_ = std::make_unique<struct ProfileInfo>();
33 int tid = syscall(SYS_gettid);
34 if (tid != -1) {
35 profileInfo_->tid = static_cast<uint64_t>(tid);
36 }
37 samplesQueue_ = new SamplesQueue();
38 }
39
~SamplesRecord()40 SamplesRecord::~SamplesRecord()
41 {
42 if (fileHandle_.is_open()) {
43 fileHandle_.close();
44 }
45 if (samplesQueue_ != nullptr) {
46 delete samplesQueue_;
47 samplesQueue_ = nullptr;
48 }
49 }
50
NodeInit()51 void SamplesRecord::NodeInit()
52 {
53 struct NodeKey nodeKey;
54 struct CpuProfileNode methodNode;
55
56 nodeKey.methodKey.methodIdentifier = reinterpret_cast<void *>(INT_MAX - 1);
57 nodeMap_.emplace(nodeKey, nodeMap_.size() + 1);
58 methodNode.parentId = 0;
59 methodNode.codeEntry.functionName = "(root)";
60 methodNode.id = 1;
61 profileInfo_->nodes[profileInfo_->nodeCount++] = methodNode;
62
63 nodeKey.methodKey.methodIdentifier = reinterpret_cast<void *>(INT_MAX - 2); // 2:program node id
64 nodeMap_.emplace(nodeKey, nodeMap_.size() + 1);
65 methodNode.codeEntry.functionName = "(program)";
66 methodNode.id = 2; // 2:program node id
67 profileInfo_->nodes[profileInfo_->nodeCount++] = methodNode;
68 profileInfo_->nodes[0].children.push_back(methodNode.id);
69
70 nodeKey.methodKey.methodIdentifier = reinterpret_cast<void *>(INT_MAX - 3); // 3:idle node id
71 nodeMap_.emplace(nodeKey, nodeMap_.size() + 1);
72 methodNode.codeEntry.functionName = "(idle)";
73 methodNode.id = 3; // 3:idle node id
74 profileInfo_->nodes[profileInfo_->nodeCount++] = methodNode;
75 profileInfo_->nodes[0].children.push_back(methodNode.id);
76 }
77
AddSample(FrameStackAndInfo * frame)78 void SamplesRecord::AddSample(FrameStackAndInfo *frame)
79 {
80 int frameStackLength = frame->frameStackLength;
81 if (frameStackLength == 0) {
82 AddEmptyStackSample(frame->timeStamp);
83 return;
84 }
85
86 FrameInfoTempToMap(frame->frameInfoTemps, frame->frameInfoTempsLength);
87
88 struct NodeKey nodeKey;
89 struct CpuProfileNode methodNode;
90 methodNode.id = 1;
91 for (; frameStackLength >= 1; frameStackLength--) {
92 nodeKey.methodKey = frame->frameStack[frameStackLength - 1];
93 methodNode.parentId = nodeKey.parentId = methodNode.id;
94 auto result = nodeMap_.find(nodeKey);
95 if (result == nodeMap_.end()) {
96 int id = static_cast<int>(nodeMap_.size() + 1);
97 nodeMap_.emplace(nodeKey, id);
98 previousId_ = methodNode.id = id;
99 methodNode.codeEntry = GetMethodInfo(nodeKey.methodKey);
100 profileInfo_->nodes[profileInfo_->nodeCount++] = methodNode;
101 profileInfo_->nodes[methodNode.parentId - 1].children.push_back(id);
102 } else {
103 previousId_ = methodNode.id = result->second;
104 }
105 }
106
107 int sampleNodeId = previousId_ == 0 ? 1 : methodNode.id;
108 int timeDelta = static_cast<int>(frame->timeStamp -
109 (previousTimeStamp_ == 0 ? profileInfo_->startTime : previousTimeStamp_));
110
111 // delete abnormal sample
112 if (timeDelta > static_cast<int>(timeDeltaThreshold_) && previousState_ != RunningState::NAPI) {
113 uint32_t size = profileInfo_->samples.size();
114 if (size > 0) {
115 profileInfo_->samples[size - 1] = PROGRAM_NODE_ID;
116 }
117 previousState_ = RunningState::OTHER;
118 }
119 StatisticStateTime(timeDelta, previousState_);
120 previousState_ = nodeKey.methodKey.state;
121 profileInfo_->nodes[sampleNodeId - 1].hitCount++;
122 profileInfo_->samples.push_back(sampleNodeId);
123 profileInfo_->timeDeltas.push_back(timeDelta);
124 previousTimeStamp_ = frame->timeStamp;
125 }
126
AddEmptyStackSample(uint64_t sampleTimeStamp)127 void SamplesRecord::AddEmptyStackSample(uint64_t sampleTimeStamp)
128 {
129 int timeDelta = static_cast<int>(sampleTimeStamp -
130 (previousTimeStamp_ == 0 ? profileInfo_->startTime : previousTimeStamp_));
131
132 // delete abnormal sample
133 if (timeDelta > static_cast<int>(timeDeltaThreshold_) && previousState_ != RunningState::NAPI) {
134 uint32_t size = profileInfo_->samples.size();
135 if (size > 0) {
136 profileInfo_->samples[size - 1] = PROGRAM_NODE_ID;
137 }
138 previousState_ = RunningState::OTHER;
139 }
140
141 StatisticStateTime(timeDelta, previousState_);
142 previousState_ = RunningState::OTHER;
143 profileInfo_->nodes[1].hitCount++;
144 profileInfo_->samples.push_back(PROGRAM_NODE_ID);
145 profileInfo_->timeDeltas.push_back(timeDelta);
146 previousTimeStamp_ = sampleTimeStamp;
147 }
148
StringifySampleData()149 void SamplesRecord::StringifySampleData()
150 {
151 sampleData_ += "{\"tid\":"
152 + std::to_string(profileInfo_->tid) + ",\"startTime\":"
153 + std::to_string(profileInfo_->startTime) + ",\"endTime\":"
154 + std::to_string(profileInfo_->stopTime) + ",";
155
156 StringifyStateTimeStatistic();
157 StringifyNodes();
158 StringifySamples();
159 }
160
StringifyStateTimeStatistic()161 void SamplesRecord::StringifyStateTimeStatistic()
162 {
163 sampleData_ += "\"gcTime\":"
164 + std::to_string(profileInfo_->gcTime) + ",\"cInterpreterTime\":"
165 + std::to_string(profileInfo_->cInterpreterTime) + ",\"asmInterpreterTime\":"
166 + std::to_string(profileInfo_->asmInterpreterTime) + ",\"aotTime\":"
167 + std::to_string(profileInfo_->aotTime) + ",\"builtinTime\":"
168 + std::to_string(profileInfo_->builtinTime) + ",\"napiTime\":"
169 + std::to_string(profileInfo_->napiTime) + ",\"arkuiEngineTime\":"
170 + std::to_string(profileInfo_->arkuiEngineTime) + ",\"runtimeTime\":"
171 + std::to_string(profileInfo_->runtimeTime) + ",\"otherTime\":"
172 + std::to_string(profileInfo_->otherTime) + ",";
173 }
174
StringifyNodes()175 void SamplesRecord::StringifyNodes()
176 {
177 sampleData_ += "\"nodes\":[";
178 size_t nodeCount = static_cast<size_t>(profileInfo_->nodeCount);
179 bool translateCallback = false;
180 if (sourceMapTranslateCallback_ != nullptr) {
181 translateCallback = true;
182 }
183 for (size_t i = 0; i < nodeCount; i++) {
184 struct CpuProfileNode node = profileInfo_->nodes[i];
185 struct FrameInfo codeEntry = node.codeEntry;
186 if (translateCallback) {
187 TranslateUrlPositionBySourceMap(codeEntry);
188 }
189 std::string url = codeEntry.url;
190 replace(url.begin(), url.end(), '\\', '/');
191 sampleData_ += "{\"id\":"
192 + std::to_string(node.id) + ",\"callFrame\":{\"functionName\":\""
193 + codeEntry.functionName + "\",\"moduleName\":\""
194 + codeEntry.moduleName + "\",\"scriptId\":\""
195 + std::to_string(codeEntry.scriptId) + "\",\"url\":\""
196 + url + "\",\"lineNumber\":"
197 + std::to_string(codeEntry.lineNumber) + ",\"columnNumber\":"
198 + std::to_string(codeEntry.columnNumber) + "},\"hitCount\":"
199 + std::to_string(node.hitCount) + ",\"children\":[";
200 CVector<int> children = node.children;
201 size_t childrenCount = children.size();
202 for (size_t j = 0; j < childrenCount; j++) {
203 sampleData_ += std::to_string(children[j]) + ",";
204 }
205 if (childrenCount > 0) {
206 sampleData_.pop_back();
207 }
208 sampleData_ += "]},";
209 }
210 sampleData_.pop_back();
211 sampleData_ += "],";
212 }
213
StringifySamples()214 void SamplesRecord::StringifySamples()
215 {
216 CVector<int> samples = profileInfo_->samples;
217 CVector<int> timeDeltas = profileInfo_->timeDeltas;
218
219 size_t samplesCount = samples.size();
220 std::string samplesIdStr = "";
221 std::string timeDeltasStr = "";
222 for (size_t i = 0; i < samplesCount; i++) {
223 samplesIdStr += std::to_string(samples[i]) + ",";
224 timeDeltasStr += std::to_string(timeDeltas[i]) + ",";
225 }
226 samplesIdStr.pop_back();
227 timeDeltasStr.pop_back();
228
229 sampleData_ += "\"samples\":[" + samplesIdStr + "],\"timeDeltas\":[" + timeDeltasStr + "]}";
230 }
231
232 /*
233 * Description: Finetune samples timedelta when stop cpuprofiler
234 * Use two-pointer algorithm to iterate samples and actively recorded napiCallTimeVec_ and napiCallAddrVec_
235 * Accumulate timeDelta and startTime to get the current timestamp
236 * When current timestamp larger than napiCall start time, then
237 * Loop backward PRE_IDX_RANGE times from previous index, find same address napi
238 * If find the same address napi, then call FindSampleAndFinetune to finetune timedelta
239 * Parameters: null
240 * Return: null
241 */
FinetuneSampleData()242 void SamplesRecord::FinetuneSampleData()
243 {
244 CVector<int> &samples = profileInfo_->samples;
245
246 size_t samplesCount = samples.size(); // samples count
247 size_t napiCallCount = napiCallTimeVec_.size(); // napiCall count
248 size_t sampleIdx = 0; // samples index
249 size_t napiCallIdx = 0; // napiCall index
250 uint64_t sampleTime = profileInfo_->startTime; // current timestamp
251
252 while (sampleIdx < samplesCount && napiCallIdx < napiCallCount) {
253 // accumulate timeDelta to get current timestamp until larger than napiCall start time
254 sampleTime += static_cast<uint64_t>(profileInfo_->timeDeltas[sampleIdx]);
255 if (sampleTime < napiCallTimeVec_[napiCallIdx]) {
256 sampleIdx++;
257 continue;
258 }
259 bool findFlag = false;
260 size_t findIdx = sampleIdx;
261 size_t preIdx = sampleIdx;
262 uint64_t preSampleTime = sampleTime -
263 static_cast<uint64_t>(profileInfo_->timeDeltas[sampleIdx]); // preIdx's timestamp
264 std::string napiFunctionAddr = napiCallAddrVec_[napiCallIdx];
265 if (sampleIdx - 1 >= 0) {
266 preIdx = sampleIdx - 1;
267 preSampleTime -= static_cast<uint64_t>(profileInfo_->timeDeltas[sampleIdx - 1]);
268 }
269 // loop backward PRE_IDX_RANGE times from previous index, find same address napi
270 for (size_t k = preIdx; k - preIdx < PRE_IDX_RANGE && k < samplesCount; k++) {
271 std::string samplesFunctionName = profileInfo_->nodes[samples[k] - 1].codeEntry.functionName;
272 preSampleTime += static_cast<uint64_t>(profileInfo_->timeDeltas[k]);
273 if (samplesFunctionName.find(napiFunctionAddr) != std::string::npos) {
274 findFlag = true;
275 findIdx = k;
276 break;
277 }
278 }
279 // found the same address napi
280 if (findFlag) {
281 FindSampleAndFinetune(findIdx, napiCallIdx, sampleIdx, preSampleTime, sampleTime);
282 } else {
283 sampleTime -= static_cast<uint64_t>(profileInfo_->timeDeltas[sampleIdx]);
284 }
285 napiCallIdx += NAPI_CALL_SETP;
286 }
287 }
288
289 /*
290 * Description: Finetune samples timedelta when find the same address napi
291 * 1. get a continuous sample: loop the samples until find the first sample that is different from findIdx
292 * 2. startIdx and endIdx: beginning and end of the continuous sample
293 * 3. call FinetuneTimeDeltas to finetune startIdx and endIdx's timedelta
294 * Parameters:
295 * 1. findIdx: sample index of same address with napi
296 * 2. napiCallIdx: napi call index
297 * 3. sampleIdx: sample index
298 * 4. startSampleTime: start sample timestamp
299 * 5. sampleTime: current timestamp
300 * Return: null
301 */
FindSampleAndFinetune(size_t findIdx,size_t napiCallIdx,size_t & sampleIdx,uint64_t startSampleTime,uint64_t & sampleTime)302 void SamplesRecord::FindSampleAndFinetune(size_t findIdx, size_t napiCallIdx, size_t &sampleIdx,
303 uint64_t startSampleTime, uint64_t &sampleTime)
304 {
305 size_t startIdx = findIdx;
306 size_t endIdx = findIdx + 1;
307 size_t samplesCount = profileInfo_->samples.size();
308 uint64_t endSampleTime = startSampleTime; // end sample timestamp
309 uint64_t startNapiTime = napiCallTimeVec_[napiCallIdx]; // call napi start timestamp
310 uint64_t endNapiTime = napiCallTimeVec_[napiCallIdx + 1]; // call napi end timestamp
311 // get a continuous sample, accumulate endSampleTime but lack last timeDeltas
312 for (; endIdx < samplesCount && profileInfo_->samples[endIdx - 1] == profileInfo_->samples[endIdx]; endIdx++) {
313 endSampleTime += static_cast<uint64_t>(profileInfo_->timeDeltas[endIdx]);
314 }
315 // finetune startIdx‘s timedelta
316 FinetuneTimeDeltas(startIdx, startNapiTime, startSampleTime, false);
317 // if the continuous sample' size is 1, endSampleTime need to adjust
318 if (startIdx + 1 == endIdx) {
319 endSampleTime -= (startSampleTime - startNapiTime);
320 }
321 // finetune endIdx's timedelta
322 FinetuneTimeDeltas(endIdx, endNapiTime, endSampleTime, true);
323 sampleTime = endSampleTime;
324 sampleIdx = endIdx;
325 }
326
327 /*
328 * Description: Finetune time deltas
329 * Parameters:
330 * 1. idx: sample index
331 * 2. napiTime: napi timestamp
332 * 3. sampleTime: sample timestamp
333 * 4. isEndSample: if is endIdx, isEndSample is true, else isEndSample is false
334 * Return: null
335 */
FinetuneTimeDeltas(size_t idx,uint64_t napiTime,uint64_t & sampleTime,bool isEndSample)336 void SamplesRecord::FinetuneTimeDeltas(size_t idx, uint64_t napiTime, uint64_t &sampleTime, bool isEndSample)
337 {
338 // timeDeltas[idx] minus a period of time, timeDeltas[idx+1] needs to add the same time
339 if (isEndSample) {
340 // if is endIdx, sampleTime add endTimeDelta is real current timestamp
341 int endTimeDelta = profileInfo_->timeDeltas[idx];
342 profileInfo_->timeDeltas[idx] -= static_cast<int>(sampleTime - napiTime) + endTimeDelta;
343 profileInfo_->timeDeltas[idx + 1] += static_cast<int>(sampleTime - napiTime) + endTimeDelta;
344 } else {
345 profileInfo_->timeDeltas[idx] -= static_cast<int>(sampleTime - napiTime);
346 profileInfo_->timeDeltas[idx + 1] += static_cast<int>(sampleTime - napiTime);
347 }
348
349 // if timeDeltas[idx] < 0, timeDeltas[idx] = MIN_TIME_DELTA
350 if (profileInfo_->timeDeltas[idx] < 0) {
351 // timeDelta is added part, needs other sample reduce to balance
352 int timeDelta = MIN_TIME_DELTA - profileInfo_->timeDeltas[idx];
353 // profileInfo_->timeDeltas[idx - 1] to reduce
354 if (idx - 1 >= 0 && profileInfo_->timeDeltas[idx - 1] > timeDelta) {
355 profileInfo_->timeDeltas[idx - 1] -= timeDelta;
356 if (isEndSample) {
357 sampleTime -= static_cast<uint64_t>(timeDelta);
358 }
359 // if timeDeltas[idx - 1] < timeDelta, timeDeltas[idx - 1] = MIN_TIME_DELTA
360 } else if (idx - 1 >= 0) {
361 // The remaining timeDeltas[idx + 1] to reduce
362 profileInfo_->timeDeltas[idx + 1] -= timeDelta - profileInfo_->timeDeltas[idx - 1] + MIN_TIME_DELTA;
363 if (isEndSample) {
364 sampleTime -= static_cast<uint64_t>(profileInfo_->timeDeltas[idx - 1] - MIN_TIME_DELTA);
365 }
366 profileInfo_->timeDeltas[idx - 1] = MIN_TIME_DELTA;
367 } else {
368 profileInfo_->timeDeltas[idx + 1] -= timeDelta;
369 }
370 profileInfo_->timeDeltas[idx] = MIN_TIME_DELTA;
371 }
372
373 // if timeDeltas[idx + 1] < 0, timeDeltas equals MIN_TIME_DELTA, timeDeltas[idx] reduce added part
374 if (profileInfo_->timeDeltas[idx + 1] < 0) {
375 int timeDelta = MIN_TIME_DELTA - profileInfo_->timeDeltas[idx];
376 profileInfo_->timeDeltas[idx] -= timeDelta;
377 profileInfo_->timeDeltas[idx + 1] = MIN_TIME_DELTA;
378 }
379 }
380
GetMethodNodeCount() const381 int SamplesRecord::GetMethodNodeCount() const
382 {
383 return profileInfo_->nodeCount;
384 }
385
GetSampleData() const386 std::string SamplesRecord::GetSampleData() const
387 {
388 return sampleData_;
389 }
390
GetMethodInfo(struct MethodKey & methodKey)391 struct FrameInfo SamplesRecord::GetMethodInfo(struct MethodKey &methodKey)
392 {
393 struct FrameInfo entry;
394 auto iter = stackInfoMap_.find(methodKey);
395 if (iter != stackInfoMap_.end()) {
396 entry = iter->second;
397 }
398 return entry;
399 }
400
AddRunningState(char * functionName,RunningState state,kungfu::DeoptType type)401 std::string SamplesRecord::AddRunningState(char *functionName, RunningState state, kungfu::DeoptType type)
402 {
403 std::string temp = functionName;
404 if (state == RunningState::AOT && type != kungfu::DeoptType::NOTCHECK) {
405 state = RunningState::AINT;
406 }
407 switch (state) {
408 case RunningState::GC:
409 temp.append("(GC)");
410 break;
411 case RunningState::CINT:
412 if (enableVMTag_) {
413 temp.append("(CINT)");
414 }
415 break;
416 case RunningState::AINT:
417 if (enableVMTag_) {
418 temp.append("(AINT)");
419 }
420 break;
421 case RunningState::AOT:
422 if (enableVMTag_) {
423 temp.append("(AOT)");
424 }
425 break;
426 case RunningState::BUILTIN:
427 temp.append("(BUILTIN)");
428 break;
429 case RunningState::NAPI:
430 temp.append("(NAPI)");
431 break;
432 case RunningState::ARKUI_ENGINE:
433 temp.append("(ARKUI_ENGINE)");
434 break;
435 case RunningState::RUNTIME:
436 if (enableVMTag_) {
437 temp.append("(RUNTIME)");
438 }
439 break;
440 default:
441 break;
442 }
443 if (type != kungfu::DeoptType::NOTCHECK && enableVMTag_) {
444 std::string typeCheckStr = "(DEOPT:" + Deoptimizier::DisplayItems(type) + ")";
445 temp.append(typeCheckStr);
446 }
447 return temp;
448 }
449
StatisticStateTime(int timeDelta,RunningState state)450 void SamplesRecord::StatisticStateTime(int timeDelta, RunningState state)
451 {
452 switch (state) {
453 case RunningState::GC: {
454 profileInfo_->gcTime += static_cast<uint64_t>(timeDelta);
455 return;
456 }
457 case RunningState::CINT: {
458 profileInfo_->cInterpreterTime += static_cast<uint64_t>(timeDelta);
459 return;
460 }
461 case RunningState::AINT: {
462 profileInfo_->asmInterpreterTime += static_cast<uint64_t>(timeDelta);
463 return;
464 }
465 case RunningState::AOT: {
466 profileInfo_->aotTime += static_cast<uint64_t>(timeDelta);
467 return;
468 }
469 case RunningState::BUILTIN: {
470 profileInfo_->builtinTime += static_cast<uint64_t>(timeDelta);
471 return;
472 }
473 case RunningState::NAPI: {
474 profileInfo_->napiTime += static_cast<uint64_t>(timeDelta);
475 return;
476 }
477 case RunningState::ARKUI_ENGINE: {
478 profileInfo_->arkuiEngineTime += static_cast<uint64_t>(timeDelta);
479 return;
480 }
481 case RunningState::RUNTIME: {
482 profileInfo_->runtimeTime += static_cast<uint64_t>(timeDelta);
483 return;
484 }
485 default: {
486 profileInfo_->otherTime += static_cast<uint64_t>(timeDelta);
487 return;
488 }
489 }
490 }
491
SetThreadStartTime(uint64_t threadStartTime)492 void SamplesRecord::SetThreadStartTime(uint64_t threadStartTime)
493 {
494 profileInfo_->startTime = threadStartTime;
495 }
496
SetThreadStopTime()497 void SamplesRecord::SetThreadStopTime()
498 {
499 profileInfo_->stopTime = previousTimeStamp_;
500 }
501
SetStartsampleData(std::string sampleData)502 void SamplesRecord::SetStartsampleData(std::string sampleData)
503 {
504 sampleData_ += sampleData;
505 }
506
SetFileName(std::string & fileName)507 void SamplesRecord::SetFileName(std::string &fileName)
508 {
509 fileName_ = fileName;
510 }
511
GetFileName() const512 const std::string SamplesRecord::GetFileName() const
513 {
514 return fileName_;
515 }
516
ClearSampleData()517 void SamplesRecord::ClearSampleData()
518 {
519 sampleData_.clear();
520 }
521
GetProfileInfo()522 std::unique_ptr<struct ProfileInfo> SamplesRecord::GetProfileInfo()
523 {
524 return std::move(profileInfo_);
525 }
526
SetBeforeGetCallNapiStackFlag(bool flag)527 void SamplesRecord::SetBeforeGetCallNapiStackFlag(bool flag)
528 {
529 beforeCallNapi_.store(flag);
530 }
531
GetBeforeGetCallNapiStackFlag()532 bool SamplesRecord::GetBeforeGetCallNapiStackFlag()
533 {
534 return beforeCallNapi_.load();
535 }
536
SetAfterGetCallNapiStackFlag(bool flag)537 void SamplesRecord::SetAfterGetCallNapiStackFlag(bool flag)
538 {
539 afterCallNapi_.store(flag);
540 }
541
GetAfterGetCallNapiStackFlag()542 bool SamplesRecord::GetAfterGetCallNapiStackFlag()
543 {
544 return afterCallNapi_.load();
545 }
546
SetCallNapiFlag(bool flag)547 void SamplesRecord::SetCallNapiFlag(bool flag)
548 {
549 callNapi_.store(flag);
550 }
551
GetCallNapiFlag()552 bool SamplesRecord::GetCallNapiFlag()
553 {
554 return callNapi_.load();
555 }
556
SemInit(int index,int pshared,int value)557 int SamplesRecord::SemInit(int index, int pshared, int value)
558 {
559 return sem_init(&sem_[index], pshared, value);
560 }
561
SemPost(int index)562 int SamplesRecord::SemPost(int index)
563 {
564 return sem_post(&sem_[index]);
565 }
566
SemWait(int index)567 int SamplesRecord::SemWait(int index)
568 {
569 return sem_wait(&sem_[index]);
570 }
571
SemDestroy(int index)572 int SamplesRecord::SemDestroy(int index)
573 {
574 return sem_destroy(&sem_[index]);
575 }
576
GetStackInfo() const577 const CMap<struct MethodKey, struct FrameInfo> &SamplesRecord::GetStackInfo() const
578 {
579 return stackInfoMap_;
580 }
581
InsertStackInfo(struct MethodKey & methodKey,struct FrameInfo & codeEntry)582 void SamplesRecord::InsertStackInfo(struct MethodKey &methodKey, struct FrameInfo &codeEntry)
583 {
584 stackInfoMap_.emplace(methodKey, codeEntry);
585 }
586
PushFrameStack(struct MethodKey & methodKey)587 bool SamplesRecord::PushFrameStack(struct MethodKey &methodKey)
588 {
589 if (UNLIKELY(frameStackLength_ >= MAX_STACK_SIZE)) {
590 return false;
591 }
592 frameStack_[frameStackLength_++] = methodKey;
593 return true;
594 }
595
PushNapiFrameStack(struct MethodKey & methodKey)596 bool SamplesRecord::PushNapiFrameStack(struct MethodKey &methodKey)
597 {
598 if (UNLIKELY(napiFrameStack_.size() >= MAX_STACK_SIZE)) {
599 return false;
600 }
601 napiFrameStack_.push_back(methodKey);
602 return true;
603 }
604
ClearNapiStack()605 void SamplesRecord::ClearNapiStack()
606 {
607 napiFrameStack_.clear();
608 napiFrameInfoTemps_.clear();
609 }
610
ClearNapiCall()611 void SamplesRecord::ClearNapiCall()
612 {
613 napiCallTimeVec_.clear();
614 napiCallAddrVec_.clear();
615 }
616
GetNapiFrameStackLength()617 int SamplesRecord::GetNapiFrameStackLength()
618 {
619 return napiFrameStack_.size();
620 }
621
GetGcState() const622 bool SamplesRecord::GetGcState() const
623 {
624 return gcState_.load();
625 }
626
SetGcState(bool gcState)627 void SamplesRecord::SetGcState(bool gcState)
628 {
629 gcState_.store(gcState);
630 }
631
GetRuntimeState() const632 bool SamplesRecord::GetRuntimeState() const
633 {
634 return runtimeState_.load();
635 }
636
SetRuntimeState(bool runtimeState)637 void SamplesRecord::SetRuntimeState(bool runtimeState)
638 {
639 runtimeState_.store(runtimeState);
640 }
641
GetIsStart() const642 bool SamplesRecord::GetIsStart() const
643 {
644 return isStart_.load();
645 }
646
SetIsStart(bool isStart)647 void SamplesRecord::SetIsStart(bool isStart)
648 {
649 isStart_.store(isStart);
650 }
651
PushStackInfo(const FrameInfoTemp & frameInfoTemp)652 bool SamplesRecord::PushStackInfo(const FrameInfoTemp &frameInfoTemp)
653 {
654 if (UNLIKELY(frameInfoTempLength_ >= MAX_STACK_SIZE)) {
655 return false;
656 }
657 frameInfoTemps_[frameInfoTempLength_++] = frameInfoTemp;
658 return true;
659 }
660
PushNapiStackInfo(const FrameInfoTemp & frameInfoTemp)661 bool SamplesRecord::PushNapiStackInfo(const FrameInfoTemp &frameInfoTemp)
662 {
663 if (UNLIKELY(napiFrameInfoTemps_.size() == MAX_STACK_SIZE)) {
664 return false;
665 }
666 napiFrameInfoTemps_.push_back(frameInfoTemp);
667 return true;
668 }
669
GetModuleName(char * recordName)670 std::string SamplesRecord::GetModuleName(char *recordName)
671 {
672 std::string recordNameStr = recordName;
673 std::string::size_type atPos = recordNameStr.find("@");
674 if (atPos == std::string::npos) {
675 return "";
676 }
677
678 std::string::size_type slashPos = recordNameStr.rfind("/", atPos);
679 if (slashPos == std::string::npos) {
680 return "";
681 }
682
683 return recordNameStr.substr(slashPos + 1, atPos - slashPos - 1);
684 }
685
FrameInfoTempToMap(FrameInfoTemp * frameInfoTemps,int frameInfoTempLength)686 void SamplesRecord::FrameInfoTempToMap(FrameInfoTemp *frameInfoTemps, int frameInfoTempLength)
687 {
688 if (frameInfoTempLength == 0) {
689 return;
690 }
691 struct FrameInfo frameInfo;
692 for (int i = 0; i < frameInfoTempLength; ++i) {
693 frameInfo.url = frameInfoTemps[i].url;
694 auto iter = scriptIdMap_.find(frameInfo.url);
695 if (iter == scriptIdMap_.end()) {
696 scriptIdMap_.emplace(frameInfo.url, scriptIdMap_.size() + 1);
697 frameInfo.scriptId = static_cast<int>(scriptIdMap_.size());
698 } else {
699 frameInfo.scriptId = iter->second;
700 }
701 frameInfo.functionName = AddRunningState(frameInfoTemps[i].functionName,
702 frameInfoTemps[i].methodKey.state,
703 frameInfoTemps[i].methodKey.deoptType);
704 if (strlen(frameInfoTemps[i].recordName) != 0) {
705 frameInfo.moduleName = GetModuleName(frameInfoTemps[i].recordName);
706 }
707 frameInfo.columnNumber = frameInfoTemps[i].columnNumber;
708 frameInfo.lineNumber = frameInfoTemps[i].lineNumber;
709 stackInfoMap_.emplace(frameInfoTemps[i].methodKey, frameInfo);
710 }
711 frameInfoTempLength_ = 0;
712 }
713
NapiFrameInfoTempToMap()714 void SamplesRecord::NapiFrameInfoTempToMap()
715 {
716 size_t length = napiFrameInfoTemps_.size();
717 if (length == 0) {
718 return;
719 }
720 struct FrameInfo frameInfo;
721 for (size_t i = 0; i < length; ++i) {
722 frameInfo.url = napiFrameInfoTemps_[i].url;
723 auto iter = scriptIdMap_.find(frameInfo.url);
724 if (iter == scriptIdMap_.end()) {
725 scriptIdMap_.emplace(frameInfo.url, scriptIdMap_.size() + 1);
726 frameInfo.scriptId = static_cast<int>(scriptIdMap_.size());
727 } else {
728 frameInfo.scriptId = iter->second;
729 }
730 frameInfo.functionName = AddRunningState(napiFrameInfoTemps_[i].functionName,
731 napiFrameInfoTemps_[i].methodKey.state,
732 napiFrameInfoTemps_[i].methodKey.deoptType);
733 if (strlen(napiFrameInfoTemps_[i].recordName) != 0) {
734 frameInfo.moduleName = GetModuleName(napiFrameInfoTemps_[i].recordName);
735 }
736 frameInfo.columnNumber = napiFrameInfoTemps_[i].columnNumber;
737 frameInfo.lineNumber = napiFrameInfoTemps_[i].lineNumber;
738 stackInfoMap_.emplace(napiFrameInfoTemps_[i].methodKey, frameInfo);
739 }
740 }
741
GetframeStackLength() const742 int SamplesRecord::GetframeStackLength() const
743 {
744 return frameStackLength_;
745 }
746
RecordCallNapiTime(uint64_t currentTime)747 void SamplesRecord::RecordCallNapiTime(uint64_t currentTime)
748 {
749 napiCallTimeVec_.emplace_back(currentTime);
750 }
751
RecordCallNapiAddr(const std::string & methodAddr)752 void SamplesRecord::RecordCallNapiAddr(const std::string &methodAddr)
753 {
754 napiCallAddrVec_.emplace_back(methodAddr);
755 }
756
PostFrame()757 void SamplesRecord::PostFrame()
758 {
759 samplesQueue_->PostFrame(frameInfoTemps_, frameStack_, frameInfoTempLength_, frameStackLength_);
760 }
761
PostNapiFrame()762 void SamplesRecord::PostNapiFrame()
763 {
764 samplesQueue_->PostNapiFrame(napiFrameInfoTemps_, napiFrameStack_);
765 }
766
ResetFrameLength()767 void SamplesRecord::ResetFrameLength()
768 {
769 frameStackLength_ = 0;
770 frameInfoTempLength_ = 0;
771 }
772
GetCallTimeStamp()773 uint64_t SamplesRecord::GetCallTimeStamp()
774 {
775 return callTimeStamp_;
776 }
777
SetCallTimeStamp(uint64_t timeStamp)778 void SamplesRecord::SetCallTimeStamp(uint64_t timeStamp)
779 {
780 callTimeStamp_ = timeStamp;
781 }
782
TranslateUrlPositionBySourceMap(struct FrameInfo & codeEntry)783 void SamplesRecord::TranslateUrlPositionBySourceMap(struct FrameInfo &codeEntry)
784 {
785 if (codeEntry.url.empty()) {
786 return;
787 }
788 if (!sourceMapTranslateCallback_(codeEntry.url, codeEntry.lineNumber, codeEntry.columnNumber)) {
789 size_t find = codeEntry.url.rfind("_.js");
790 if (find == std::string::npos) {
791 size_t start = codeEntry.url.find("entry/");
792 size_t end = codeEntry.url.rfind(".ets");
793 if (start != std::string::npos && end != std::string::npos) {
794 std::string key = codeEntry.url.substr(start + SUB_LEN, end - start - SUB_LEN);
795 codeEntry.url = JS_PATH + key + ".js";
796 }
797 }
798 }
799 }
800
AddStartTraceEvent()801 void SamplesRecord::AddStartTraceEvent()
802 {
803 uint64_t tid = profileInfo_->tid;
804 auto vm = CpuProfiler::GetVmbyTid(tid);
805 if (vm == nullptr) {
806 LOG_ECMA(ERROR) << "CpuProfiler get vm from tid failed";
807 return;
808 }
809
810 Tracing *tracing = vm->GetTracing();
811 if (tracing == nullptr || !tracing->IsTracing()) {
812 return;
813 }
814
815 tracing->TraceEventRecordCpuProfilerStart(profileInfo_.get());
816 }
817
AddTraceEvent(bool isFinish)818 void SamplesRecord::AddTraceEvent(bool isFinish)
819 {
820 const uint32_t samplesCountPerEvent = 100;
821 if (!isFinish && (profileInfo_->samples.size() - traceEventSamplePos_ < samplesCountPerEvent)) {
822 return;
823 }
824
825 uint64_t tid = profileInfo_->tid;
826 auto vm = CpuProfiler::GetVmbyTid(tid);
827 if (vm == nullptr) {
828 LOG_ECMA(ERROR) << "CpuProfiler get vm from tid failed";
829 return;
830 }
831
832 Tracing *tracing = vm->GetTracing();
833 if (tracing == nullptr || !tracing->IsTracing()) {
834 return;
835 }
836
837 tracing->TraceEventRecordCpuProfiler(profileInfo_.get(), traceEventNodePos_, traceEventSamplePos_);
838
839 if (isFinish) {
840 tracing->TraceEventRecordCpuProfilerEnd(profileInfo_.get());
841 }
842 }
843
844 // SamplesQueue
PostFrame(FrameInfoTemp * frameInfoTemps,MethodKey * frameStack,int frameInfoTempsLength,int frameStackLength)845 void SamplesQueue::PostFrame(FrameInfoTemp *frameInfoTemps, MethodKey *frameStack,
846 int frameInfoTempsLength, int frameStackLength)
847 {
848 LockHolder holder(mtx_);
849 if (!IsFull()) {
850 // frameInfoTemps
851 for (int i = 0; i < frameInfoTempsLength; i++) {
852 CheckAndCopy(frames_[rear_].frameInfoTemps[i].functionName,
853 sizeof(frames_[rear_].frameInfoTemps[i].functionName), frameInfoTemps[i].functionName);
854 CheckAndCopy(frames_[rear_].frameInfoTemps[i].recordName,
855 sizeof(frames_[rear_].frameInfoTemps[i].recordName), frameInfoTemps[i].recordName);
856 frames_[rear_].frameInfoTemps[i].columnNumber = frameInfoTemps[i].columnNumber;
857 frames_[rear_].frameInfoTemps[i].lineNumber = frameInfoTemps[i].lineNumber;
858 frames_[rear_].frameInfoTemps[i].scriptId = frameInfoTemps[i].scriptId;
859 CheckAndCopy(frames_[rear_].frameInfoTemps[i].url,
860 sizeof(frames_[rear_].frameInfoTemps[i].url), frameInfoTemps[i].url);
861 frames_[rear_].frameInfoTemps[i].methodKey.methodIdentifier = frameInfoTemps[i].methodKey.methodIdentifier;
862 frames_[rear_].frameInfoTemps[i].methodKey.state = frameInfoTemps[i].methodKey.state;
863 frames_[rear_].frameInfoTemps[i].methodKey.lineNumber = frameInfoTemps[i].methodKey.lineNumber;
864 }
865 // frameStack
866 for (int i = 0; i < frameStackLength; i++) {
867 frames_[rear_].frameStack[i].methodIdentifier = frameStack[i].methodIdentifier;
868 frames_[rear_].frameStack[i].state = frameStack[i].state;
869 frames_[rear_].frameStack[i].lineNumber = frameStack[i].lineNumber;
870 }
871 // frameStackLength
872 frames_[rear_].frameStackLength = frameStackLength;
873 // frameInfoTempsLength
874 frames_[rear_].frameInfoTempsLength = frameInfoTempsLength;
875 // timeStamp
876 frames_[rear_].timeStamp = SamplingProcessor::GetMicrosecondsTimeStamp();
877
878 rear_ = (rear_ + 1) % QUEUE_CAPACITY;
879 }
880 }
881
PostNapiFrame(CVector<FrameInfoTemp> & napiFrameInfoTemps,CVector<MethodKey> & napiFrameStack)882 void SamplesQueue::PostNapiFrame(CVector<FrameInfoTemp> &napiFrameInfoTemps,
883 CVector<MethodKey> &napiFrameStack)
884 {
885 LockHolder holder(mtx_);
886 if (!IsFull()) {
887 size_t frameInfoTempsLength = napiFrameInfoTemps.size();
888 size_t frameStackLength = napiFrameStack.size();
889 // napiFrameInfoTemps
890 for (size_t i = 0; i < frameInfoTempsLength; i++) {
891 CheckAndCopy(frames_[rear_].frameInfoTemps[i].functionName,
892 sizeof(frames_[rear_].frameInfoTemps[i].functionName), napiFrameInfoTemps[i].functionName);
893 CheckAndCopy(frames_[rear_].frameInfoTemps[i].recordName,
894 sizeof(frames_[rear_].frameInfoTemps[i].recordName), napiFrameInfoTemps[i].recordName);
895 frames_[rear_].frameInfoTemps[i].columnNumber = napiFrameInfoTemps[i].columnNumber;
896 frames_[rear_].frameInfoTemps[i].lineNumber = napiFrameInfoTemps[i].lineNumber;
897 frames_[rear_].frameInfoTemps[i].scriptId = napiFrameInfoTemps[i].scriptId;
898 CheckAndCopy(frames_[rear_].frameInfoTemps[i].url,
899 sizeof(frames_[rear_].frameInfoTemps[i].url), napiFrameInfoTemps[i].url);
900 frames_[rear_].frameInfoTemps[i].methodKey.methodIdentifier =
901 napiFrameInfoTemps[i].methodKey.methodIdentifier;
902 frames_[rear_].frameInfoTemps[i].methodKey.state = napiFrameInfoTemps[i].methodKey.state;
903 frames_[rear_].frameInfoTemps[i].methodKey.lineNumber = napiFrameInfoTemps[i].methodKey.lineNumber;
904 }
905 // napiFrameStack
906 for (size_t i = 0; i < frameStackLength; i++) {
907 frames_[rear_].frameStack[i].methodIdentifier = napiFrameStack[i].methodIdentifier;
908 frames_[rear_].frameStack[i].state = napiFrameStack[i].state;
909 frames_[rear_].frameStack[i].lineNumber = napiFrameStack[i].lineNumber;
910 }
911 // frameStackLength
912 frames_[rear_].frameStackLength = frameStackLength;
913 // frameInfoTempsLength
914 frames_[rear_].frameInfoTempsLength = frameInfoTempsLength;
915 // timeStamp
916 frames_[rear_].timeStamp = SamplingProcessor::GetMicrosecondsTimeStamp();
917
918 rear_ = (rear_ + 1) % QUEUE_CAPACITY;
919 }
920 }
921
PopFrame()922 FrameStackAndInfo *SamplesQueue::PopFrame()
923 {
924 LockHolder holder(mtx_);
925 if (!IsEmpty()) {
926 FrameStackAndInfo *frame = &frames_[front_];
927 front_ = (front_ + 1) % QUEUE_CAPACITY;
928 return frame;
929 }
930 return nullptr;
931 }
932
IsEmpty()933 bool SamplesQueue::IsEmpty()
934 {
935 return front_ == rear_;
936 }
937
IsFull()938 bool SamplesQueue::IsFull()
939 {
940 return (rear_ + 1) % QUEUE_CAPACITY == front_;
941 }
942
GetSize()943 int SamplesQueue::GetSize()
944 {
945 return (rear_ + QUEUE_CAPACITY - front_) % QUEUE_CAPACITY;
946 }
947
GetFrontIndex()948 int SamplesQueue::GetFrontIndex()
949 {
950 return front_;
951 }
952
GetRearIndex()953 int SamplesQueue::GetRearIndex()
954 {
955 return rear_;
956 }
957
CheckAndCopy(char * dest,size_t length,const char * src) const958 bool SamplesQueue::CheckAndCopy(char *dest, size_t length, const char *src) const
959 {
960 int srcLength = strlen(src);
961 if (length <= static_cast<size_t>(srcLength) || strcpy_s(dest, srcLength + 1, src) != EOK) {
962 LOG_ECMA(ERROR) << "SamplesQueue PostFrame strcpy_s failed, maybe srcLength more than destLength";
963 return false;
964 }
965 dest[srcLength] = '\0';
966 return true;
967 }
968 } // namespace panda::ecmascript
969