1 /*
2 * Copyright (c) 2023 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/tracing/tracing.h"
17 #include "ecmascript/debugger/js_debugger_manager.h"
18
19 namespace panda::ecmascript {
GetEventBufferSize()20 uint64_t TraceEvent::GetEventBufferSize()
21 {
22 uint64_t size = sizeof(*this) + cat_.size() + name_.size() + ph_.size() + args_.size() + s_.size() + id_.size();
23 if (cpuProfileArgs_.has_value()) {
24 size += cpuProfileArgs_.value().nodes.size() * sizeof(int);
25 size += cpuProfileArgs_.value().samples.size() * sizeof(int);
26 size += cpuProfileArgs_.value().timeDeltas.size() * sizeof(int);
27 }
28 return size;
29 }
30
Tracing(const EcmaVM *vm)31 Tracing::Tracing(const EcmaVM *vm) : vm_(vm)
32 {
33 traceEvents_ = std::make_unique<std::vector<TraceEvent>>();
34 traceEventsCpuProfiler_ = std::make_unique<std::vector<TraceEvent>>();
35 }
36
~Tracing()37 Tracing::~Tracing()
38 {
39 }
40
StartTracing(std::string &categories)41 void Tracing::StartTracing(std::string &categories)
42 {
43 if (isTracing_) {
44 return;
45 }
46
47 categories_ = categories;
48 if (categories_.find("cpu_profiler") != std::string::npos) {
49 panda::JSNApi::SetProfilerState(vm_, true);
50 panda::DFXJSNApi::StartCpuProfilerForInfo(vm_);
51 }
52
53 tid_ = static_cast<pthread_t>(syscall(SYS_gettid));
54 isTracing_ = true;
55 vm_->GetJsDebuggerManager()->GetNotificationManager()->AddListener(this);
56 vm_->GetJSThread()->SetIsTracing(true);
57
58 TraceEventRecordTracingStart();
59 return;
60 }
61
StopTracing()62 std::unique_ptr<std::vector<TraceEvent>> Tracing::StopTracing()
63 {
64 isTracing_ = false;
65 vm_->GetJsDebuggerManager()->GetNotificationManager()->RemoveListener(this);
66 if (categories_.find("cpu_profiler") != std::string::npos) {
67 std::unique_ptr<ProfileInfo> profileInfo = panda::DFXJSNApi::StopCpuProfilerForInfo(vm_);
68 panda::JSNApi::SetProfilerState(vm_, false);
69 if (profileInfo) {
70 TraceEventUpdateCpuProfiler(profileInfo.get());
71 }
72 }
73 vm_->GetJSThread()->SetIsTracing(false);
74 return std::move(traceEvents_);
75 }
76
GetTimeStamp()77 uint64_t Tracing::GetTimeStamp()
78 {
79 const int USEC_PER_SEC = 1000 * 1000;
80 const int NSEC_PER_USEC = 1000;
81 struct timespec time;
82 clock_gettime(CLOCK_MONOTONIC, &time);
83 return time.tv_sec * USEC_PER_SEC + time.tv_nsec / NSEC_PER_USEC;
84 }
85
RecordTraceEvent(TraceEvent &event)86 void Tracing::RecordTraceEvent(TraceEvent &event)
87 {
88 bufferSize_ += event.GetEventBufferSize();
89
90 std::lock_guard<std::mutex> lock(lock_);
91 traceEvents_->emplace_back(event);
92 }
93
TraceEventRecordTracingStart()94 void Tracing::TraceEventRecordTracingStart()
95 {
96 int64_t ts = static_cast<int64_t>(GetTimeStamp());
97 std::string args = "{\"data\":{\"frameTreeNodeId\":1,\"frames\":[{";
98 args += "\"frame\":\"0\",\"name\":\"\",";
99 args += "\"processId\":" + std::to_string(getpid()) + ",";
100 args += "\"url\":\"https://url not exist/\"}],";
101 args += "\"persistentIds\":true}}";
102
103 TraceEvent event("disabled-by-default-devtools.timeline", "TracingStartedInBrowser", "I", getpid(), tid_);
104 event.SetTs(ts);
105 event.SetTts(ts);
106 event.SetS("t");
107 event.SetArgs(args);
108
109 RecordTraceEvent(event);
110 }
111
TraceEventRecordMemory()112 void Tracing::TraceEventRecordMemory()
113 {
114 if (!isTracing_) {
115 return;
116 }
117
118 int64_t ts = static_cast<int64_t>(GetTimeStamp());
119 TraceEvent event("disabled-by-default-devtools.timeline", "UpdateCounters", "I", getpid(), tid_);
120 event.SetTs(ts);
121 event.SetTts(ts);
122 event.SetS("t");
123 std::string args = "{\"data\":{\"jsHeapSizeUsed\":" + std::to_string(DFXJSNApi::GetHeapUsedSize(vm_)) + "}}";
124 event.SetArgs(args);
125
126 RecordTraceEvent(event);
127 }
128
TraceEventRecordCpuProfilerStart(struct ProfileInfo* profileInfo)129 void Tracing::TraceEventRecordCpuProfilerStart(struct ProfileInfo* profileInfo)
130 {
131 int64_t ts = static_cast<int64_t>(GetTimeStamp());
132 std::string args = "{\"data\":{\"startTime\":" + std::to_string(profileInfo->startTime) + "}}";
133 TraceEvent event("disabled-by-default-v8.cpu_profiler", "Profile", "P", getpid(), tid_);
134 event.SetTs(ts);
135 event.SetTts(ts);
136 event.SetId("0x1");
137 event.SetArgs(args);
138
139 bufferSize_ += event.GetEventBufferSize();
140 traceEventsCpuProfiler_->emplace_back(event);
141 }
142
TraceEventRecordCpuProfiler(struct ProfileInfo* profileInfo, int &nodePos, uint32_t &samplePos)143 void Tracing::TraceEventRecordCpuProfiler(struct ProfileInfo* profileInfo, int &nodePos, uint32_t &samplePos)
144 {
145 if (!isTracing_) {
146 return;
147 }
148
149 int64_t ts = static_cast<int64_t>(GetTimeStamp());
150 TraceEvent event("disabled-by-default-v8.cpu_profiler", "ProfileChunk", "P", getpid(), tid_);
151 event.SetTs(ts);
152 event.SetTts(ts);
153 event.SetId("0x1");
154
155 struct TraceEventCpuProfileArg args;
156 if (nodePos >= 0 && profileInfo->nodeCount > nodePos) {
157 for (int i = nodePos; i < profileInfo->nodeCount; ++i) {
158 args.nodes.emplace_back(profileInfo->nodes[i].id);
159 }
160 nodePos = profileInfo->nodeCount;
161 }
162
163 std::copy(profileInfo->samples.begin() + samplePos, profileInfo->samples.end(),
164 std::back_inserter(args.samples));
165 std::copy(profileInfo->timeDeltas.begin() + samplePos, profileInfo->timeDeltas.end(),
166 std::back_inserter(args.timeDeltas));
167 samplePos = profileInfo->samples.size();
168
169 event.SetCpuProfileArgs(args);
170
171 bufferSize_ += event.GetEventBufferSize();
172 traceEventsCpuProfiler_->emplace_back(event);
173 }
174
TraceEventUpdateCpuProfiler(struct ProfileInfo *profileInfo)175 void Tracing::TraceEventUpdateCpuProfiler(struct ProfileInfo *profileInfo)
176 {
177 for (auto &event : *traceEventsCpuProfiler_) {
178 if (!event.cpuProfileArgs_.has_value()) {
179 traceEvents_->emplace_back(event);
180 continue;
181 }
182
183 struct TraceEventCpuProfileArg &cpuProfileArg = event.cpuProfileArgs_.value();
184 std::string args = "{\"data\":{\"cpuProfile\":{";
185 // nodes
186 if (cpuProfileArg.nodes.size() > 0) {
187 args += "\"nodes\": [";
188 for (auto &nodeId : cpuProfileArg.nodes) {
189 struct CpuProfileNode &nodeInfo = profileInfo->nodes[nodeId - 1];
190 args += "{\"callFrame\":{\"codeType\":\"JS\",";
191 if (nodeInfo.codeEntry.columnNumber != -1) {
192 args += "\"columnNumber\":" + std::to_string(nodeInfo.codeEntry.columnNumber) + ",";
193 }
194 args += "\"functionName\":\"" + nodeInfo.codeEntry.functionName + "\",";
195 if (nodeInfo.codeEntry.lineNumber != -1) {
196 args += "\"lineNumber\":" + std::to_string(nodeInfo.codeEntry.lineNumber) + ",";
197 }
198 args += "\"scriptId\":" + std::to_string(nodeInfo.codeEntry.scriptId) + ",";
199 if (nodeInfo.codeEntry.scriptId != 0) {
200 args += "\"url\":\"" + nodeInfo.codeEntry.url + "\"";
201 } else {
202 args.pop_back();
203 }
204 args += "},";
205 args += "\"id\":" + std::to_string(nodeInfo.id) + ",";
206 if (nodeInfo.parentId != 0) {
207 args += "\"parent\":" + std::to_string(nodeInfo.parentId) + ",";
208 }
209 args += "\"hitCount\":" + std::to_string(nodeInfo.hitCount) + ",";
210 args += "\"children\":[";
211 for (auto &it : nodeInfo.children) {
212 args += std::to_string(it) + ",";
213 }
214 if (nodeInfo.children.size() != 0) {
215 args.pop_back();
216 }
217 args += "]},";
218 }
219 args.pop_back();
220 args += "],";
221 }
222
223 // samples
224 args += "\"samples\": [";
225 for (auto sample : cpuProfileArg.samples) {
226 args += std::to_string(sample) + ",";
227 }
228 args.pop_back();
229 args += "]},";
230
231 // lines
232 args += "\"lines\": [";
233 for (auto sample : cpuProfileArg.samples) {
234 args += std::to_string(profileInfo->nodes[profileInfo->samples[sample - 1]].codeEntry.lineNumber + 1) + ",";
235 }
236 args.pop_back();
237 args += "],";
238
239 // timeDeltas
240 args += "\"timeDeltas\": [";
241 for (auto timeDelta : cpuProfileArg.timeDeltas) {
242 args += std::to_string(timeDelta) + ",";
243 }
244 args.pop_back();
245 args += "]}}";
246
247 event.SetArgs(args);
248 traceEvents_->emplace_back(event);
249 }
250 }
251
TraceEventRecordCpuProfilerEnd(struct ProfileInfo* profileInfo)252 void Tracing::TraceEventRecordCpuProfilerEnd(struct ProfileInfo* profileInfo)
253 {
254 int64_t ts = static_cast<int64_t>(GetTimeStamp());
255 std::string args = "{\"data\":{\"endTime\":" + std::to_string(profileInfo->stopTime) + "}}";
256 TraceEvent event("disabled-by-default-v8.cpu_profiler", "ProfileChunk", "P", getpid(), tid_);
257 event.SetTs(ts);
258 event.SetTts(ts);
259 event.SetId("0x1");
260 event.SetArgs(args);
261
262 bufferSize_ += event.GetEventBufferSize();
263 traceEventsCpuProfiler_->emplace_back(event);
264 }
265
GetBufferUseage(double &percentFull, uint32_t &eventCount, double &value)266 void Tracing::GetBufferUseage(double &percentFull, uint32_t &eventCount, double &value)
267 {
268 percentFull = (bufferSize_ >= maxBufferSize_) ? 1.0 : static_cast<double>(bufferSize_) / maxBufferSize_;
269 eventCount = 0;
270 value = percentFull;
271 }
272
LoadModule([[maybe_unused]] std::string_view name, [[maybe_unused]] std::string_view)273 void Tracing::LoadModule([[maybe_unused]] std::string_view name, [[maybe_unused]] std::string_view)
274 {
275 return;
276 }
BytecodePcChanged([[maybe_unused]] JSThread *thread, [[maybe_unused]] JSHandle<Method> method, [[maybe_unused]] uint32_t bcOffset)277 void Tracing::BytecodePcChanged([[maybe_unused]] JSThread *thread, [[maybe_unused]] JSHandle<Method> method,
278 [[maybe_unused]] uint32_t bcOffset)
279 {
280 return;
281 }
HandleDebuggerStmt([[maybe_unused]] JSHandle<Method> method, [[maybe_unused]] uint32_t bcOffset)282 bool Tracing::HandleDebuggerStmt([[maybe_unused]] JSHandle<Method> method, [[maybe_unused]] uint32_t bcOffset)
283 {
284 return true;
285 }
VmStart()286 void Tracing::VmStart()
287 {
288 return;
289 }
VmDeath()290 void Tracing::VmDeath()
291 {
292 return;
293 }
NativeCalling([[maybe_unused]] const void *nativeAddress)294 void Tracing::NativeCalling([[maybe_unused]] const void *nativeAddress)
295 {
296 return;
297 }
NativeReturn([[maybe_unused]] const void *nativeAddress)298 void Tracing::NativeReturn([[maybe_unused]] const void *nativeAddress)
299 {
300 return;
301 }
MethodEntry([[maybe_unused]] JSHandle<Method> method, [[maybe_unused]] JSHandle<JSTaggedValue> envHandle)302 void Tracing::MethodEntry([[maybe_unused]] JSHandle<Method> method, [[maybe_unused]] JSHandle<JSTaggedValue> envHandle)
303 {
304 return;
305 }
MethodExit([[maybe_unused]] JSHandle<Method> method)306 void Tracing::MethodExit([[maybe_unused]] JSHandle<Method> method)
307 {
308 TraceEventRecordMemory();
309 return;
310 }
311 } // namespace panda::ecmascript
312