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/cpu_profiler/profile_generator.h"
17
18 #include <climits>
19 #include "ecmascript/cpu_profiler/cpu_profiler.h"
20 #include "ecmascript/interpreter/interpreter.h"
21 namespace panda::ecmascript {
22 bool ProfileGenerator::staticGcState_ = false;
ProfileGenerator()23 ProfileGenerator::ProfileGenerator()
24 {
25 stackTopLines_.push_back(0);
26 struct MethodKey methodkey;
27 struct MethodNode methodNode;
28 methodkey.method = reinterpret_cast<JSMethod*>(INT_MAX - 1);
29 methodMap_.insert(std::make_pair(methodkey, methodMap_.size() + 1));
30 methodNode.parentId = 0;
31 methodNode.codeEntry.codeType = "JS";
32 methodNodes_.push_back(methodNode);
33 }
34
~ProfileGenerator()35 ProfileGenerator::~ProfileGenerator()
36 {
37 if (fileHandle_.is_open()) {
38 fileHandle_.close();
39 }
40 }
41
AddSample(CVector<JSMethod * > sample,uint64_t sampleTimeStamp)42 void ProfileGenerator::AddSample(CVector<JSMethod *> sample, uint64_t sampleTimeStamp)
43 {
44 static int PreviousId = 0;
45 struct MethodKey methodkey;
46 struct MethodNode methodNode;
47 if (staticGcState_) {
48 methodkey.method = reinterpret_cast<JSMethod*>(INT_MAX);
49 methodNode.parentId = methodkey.parentId = PreviousId;
50 auto result = methodMap_.find(methodkey);
51 if (result == methodMap_.end()) {
52 methodNode.id = methodMap_.size() + 1;
53 methodMap_.insert(std::make_pair(methodkey, methodNode.id));
54 methodNode.codeEntry = GetGcInfo();
55 stackTopLines_.push_back(0);
56 methodNodes_.push_back(methodNode);
57 } else {
58 methodNode.id = result->second;
59 }
60 staticGcState_ = false;
61 } else {
62 for (auto method = sample.rbegin(); method != sample.rend(); method++) {
63 methodkey.method = *method;
64 if (method == sample.rbegin()) {
65 methodNode.id = 1;
66 continue;
67 } else {
68 methodNode.parentId = methodkey.parentId = methodNode.id;
69 }
70 auto result = methodMap_.find(methodkey);
71 if (result == methodMap_.end()) {
72 int id = methodMap_.size() + 1;
73 methodMap_.insert(std::make_pair(methodkey, id));
74 PreviousId = methodNode.id = id;
75 methodNode.codeEntry = GetMethodInfo(methodkey.method);
76 stackTopLines_.push_back(methodNode.codeEntry.lineNumber);
77 methodNodes_.push_back(methodNode);
78 } else {
79 PreviousId = methodNode.id = result->second;
80 }
81 }
82 }
83 static uint64_t threadStartTime = 0;
84 struct SampleInfo sampleInfo;
85 sampleInfo.id = methodNode.id == 0 ? PreviousId = 1, 1 : methodNode.id;
86 sampleInfo.line = stackTopLines_[methodNode.id];
87 if (threadStartTime == 0) {
88 sampleInfo.timeStamp = sampleTimeStamp - threadStartTime_;
89 } else {
90 sampleInfo.timeStamp = sampleTimeStamp - threadStartTime;
91 }
92 samples_.push_back(sampleInfo);
93 threadStartTime = sampleTimeStamp;
94 }
95
WriteAddNodes()96 void ProfileGenerator::WriteAddNodes()
97 {
98 sampleData_ += "{\"args\":{\"data\":{\"cpuProfile\":{\"nodes\":[";
99 for (auto it : methodNodes_) {
100 sampleData_ += "{\"callFrame\":{\"codeType\":\"" + it.codeEntry.codeType + "\",";
101 if (it.parentId == 0) {
102 sampleData_ += "\"functionName\":\"(root)\",\"scriptId\":0},\"id\":1},";
103 continue;
104 }
105 if (it.codeEntry.codeType == "other" || it.codeEntry.codeType == "jsvm") {
106 sampleData_ += "\"functionName\":\"" + it.codeEntry.functionName + "\",\"scriptId\":" +
107 std::to_string(it.codeEntry.scriptId) + "},\"id\":" + std::to_string(it.id);
108 } else {
109 sampleData_ += "\"columnNumber\":" + std::to_string(it.codeEntry.columnNumber) +
110 ",\"functionName\":\"" + it.codeEntry.functionName + "\",\"lineNumber\":\"" +
111 std::to_string(it.codeEntry.lineNumber) + "\",\"scriptId\":" +
112 std::to_string(it.codeEntry.scriptId) + ",\"url\":\"" + it.codeEntry.url +
113 "\"},\"id\":" + std::to_string(it.id);
114 }
115 sampleData_ += ",\"parent\":" + std::to_string(it.parentId) + "},";
116 }
117 sampleData_.pop_back();
118 sampleData_ += "],\"samples\":[";
119 }
120
WriteAddSamples()121 void ProfileGenerator::WriteAddSamples()
122 {
123 if (samples_.empty()) {
124 return;
125 }
126 std::string sampleId = "";
127 std::string sampleLine = "";
128 std::string timeStamp = "";
129 for (auto it : samples_) {
130 sampleId += std::to_string(it.id) + ",";
131 sampleLine += std::to_string(it.line) + ",";
132 timeStamp += std::to_string(it.timeStamp) + ",";
133 }
134 sampleId.pop_back();
135 sampleLine.pop_back();
136 timeStamp.pop_back();
137 sampleData_ += sampleId + "]},\"lines\":[" + sampleLine + "],\"timeDeltas\":[" + timeStamp + "]}},";
138 }
139
WriteMethodsAndSampleInfo(bool timeEnd)140 void ProfileGenerator::WriteMethodsAndSampleInfo(bool timeEnd)
141 {
142 if (methodNodes_.size() >= 10) { // 10:Number of nodes currently stored
143 WriteAddNodes();
144 WriteAddSamples();
145 methodNodes_.clear();
146 samples_.clear();
147 } else if (samples_.size() == 100 || timeEnd) { // 100:Number of samples currently stored
148 if (!methodNodes_.empty()) {
149 WriteAddNodes();
150 WriteAddSamples();
151 methodNodes_.clear();
152 samples_.clear();
153 } else if (!samples_.empty()) {
154 sampleData_ += "{\"args\":{\"data\":{\"cpuProfile\":{\"samples\":[";
155 WriteAddSamples();
156 samples_.clear();
157 } else {
158 return;
159 }
160 }
161 sampleData_ += "\"cat\":\"disabled-by-default-ark.cpu_profiler\",\"id\":"
162 "\"0x2\",\"name\":\"ProfileChunk\",\"ph\":\"P\",\"pid\":";
163 pid_t pid = getpid();
164 pthread_t tid = syscall(SYS_gettid);
165 uint64_t ts = ProfileProcessor::GetMicrosecondsTimeStamp();
166 ts = ts % TIME_CHANGE;
167 struct timespec time = {0, 0};
168 clock_gettime(CLOCK_MONOTONIC, &time);
169 uint64_t tts = time.tv_nsec / 1000; // 1000:Nanoseconds to milliseconds.
170 sampleData_ += std::to_string(pid) + ",\"tid\":" +
171 std::to_string(tid) + ",\"ts\":" +
172 std::to_string(ts) + ",\"tts\":" +
173 std::to_string(tts) + "},\n";
174 }
175
GetMethodNodes() const176 CVector<struct MethodNode> ProfileGenerator::GetMethodNodes() const
177 {
178 return methodNodes_;
179 }
180
GetSamples() const181 CDeque<struct SampleInfo> ProfileGenerator::GetSamples() const
182 {
183 return samples_;
184 }
185
GetSampleData() const186 std::string ProfileGenerator::GetSampleData() const
187 {
188 return sampleData_;
189 }
190
GetMethodInfo(JSMethod * method)191 struct StackInfo ProfileGenerator::GetMethodInfo(JSMethod *method)
192 {
193 struct StackInfo entry;
194 auto iter = CpuProfiler::staticStackInfo_.find(method);
195 if (iter != CpuProfiler::staticStackInfo_.end()) {
196 entry = iter->second;
197 }
198 return entry;
199 }
200
GetGcInfo()201 struct StackInfo ProfileGenerator::GetGcInfo()
202 {
203 struct StackInfo gcEntry;
204 gcEntry.codeType = "jsvm";
205 gcEntry.functionName = "garbage collector";
206 return gcEntry;
207 }
208
SetThreadStartTime(uint64_t threadStartTime)209 void ProfileGenerator::SetThreadStartTime(uint64_t threadStartTime)
210 {
211 threadStartTime_ = threadStartTime;
212 }
213
SetStartsampleData(std::string sampleData)214 void ProfileGenerator::SetStartsampleData(std::string sampleData)
215 {
216 sampleData_ += sampleData;
217 }
218
SetFileName(std::string & fileName)219 void ProfileGenerator::SetFileName(std::string &fileName)
220 {
221 fileName_ = fileName;
222 }
223
GetFileName() const224 const std::string ProfileGenerator::GetFileName() const
225 {
226 return fileName_;
227 }
228
ClearSampleData()229 void ProfileGenerator::ClearSampleData()
230 {
231 sampleData_.clear();
232 }
233 } // namespace panda::ecmascript