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