• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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