1 /**
2 * Copyright (c) 2021-2024 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 "trace.h"
17
18 #include "include/runtime_notification.h"
19 #include "include/runtime.h"
20 #include "include/method.h"
21 #include "include/class.h"
22 #include "plugins.h"
23 #include "runtime/include/runtime.h"
24
25 #include <string>
26
27 namespace ark {
28
29 os::memory::Mutex g_traceLock; // NOLINT(fuchsia-statically-constructed-objects)
30 Trace *volatile Trace::singletonTrace_ = nullptr; // CC-OFF(G.FMT.15) pointer CV qualifier
31 bool Trace::isTracing_ = false;
32 // NOLINTNEXTLINE(fuchsia-statically-constructed-objects)
33 LanguageContext Trace::ctx_ = LanguageContext(nullptr);
34
SystemMicroSecond()35 static uint64_t SystemMicroSecond()
36 {
37 timespec current {};
38 clock_gettime(CLOCK_MONOTONIC, ¤t);
39 // NOTE(ht): Deleting OS-specific code, move to libpandabase, issue #3210
40 return static_cast<uint64_t>(current.tv_sec) * UINT64_C(1000000) + // 1000000 - time
41 static_cast<uint64_t>(current.tv_nsec) / UINT64_C(1000); // 1000 - time
42 }
43
RealTimeSecond()44 static uint64_t RealTimeSecond()
45 {
46 timespec current {};
47 clock_gettime(CLOCK_REALTIME, ¤t);
48 // NOTE(ht): Deleting OS-specific code, move to libpandabase, issue #3210
49 return static_cast<uint64_t>(current.tv_sec);
50 }
51
ThreadCpuClock(os::thread::NativeHandleType thread,clockid_t * clockId)52 static void ThreadCpuClock(os::thread::NativeHandleType thread, clockid_t *clockId)
53 {
54 pthread_getcpuclockid(thread, clockId);
55 }
GetCpuMicroSecond()56 static uint64_t GetCpuMicroSecond()
57 {
58 auto threadSelf = os::thread::GetNativeHandle();
59 clockid_t clockId;
60 ThreadCpuClock(threadSelf, &clockId);
61 timespec current {};
62 clock_gettime(clockId, ¤t);
63 // NOTE(ht): Deleting OS-specific code, move to libpandabase, issue #3210
64 return static_cast<uint64_t>(current.tv_sec) * UINT64_C(1000000) + // 1000000 - time
65 static_cast<uint64_t>(current.tv_nsec) / UINT64_C(1000); // 1000 - time
66 }
67
Trace(PandaUniquePtr<ark::os::unix::file::File> traceFile,size_t bufferSize)68 Trace::Trace(PandaUniquePtr<ark::os::unix::file::File> traceFile, size_t bufferSize)
69 : traceFile_(std::move(traceFile)),
70 bufferSize_(std::max(TRACE_HEADER_REAL_LENGTH, bufferSize)),
71 traceStartTime_(SystemMicroSecond()),
72 baseCpuTime_(GetCpuMicroSecond()),
73 bufferOffset_(0)
74 {
75 buffer_ = MakePandaUnique<uint8_t[]>(bufferSize_); // NOLINT(modernize-avoid-c-arrays)
76 if (memset_s(buffer_.get(), bufferSize_, 0, TRACE_HEADER_LENGTH) != EOK) {
77 LOG(ERROR, RUNTIME) << __func__ << " memset error";
78 }
79 WriteDataByte(buffer_.get(), MAGIC_VALUE, numberOf4Bytes_);
80 WriteDataByte(buffer_.get() + numberOf4Bytes_, TRACE_VERSION, numberOf2Bytes_);
81 WriteDataByte(buffer_.get() + numberOf4Bytes_ + numberOf2Bytes_, TRACE_HEADER_LENGTH, numberOf2Bytes_);
82 WriteDataByte(buffer_.get() + numberOf8Bytes_, traceStartTime_, numberOf8Bytes_);
83 WriteDataByte(buffer_.get() + numberOf8Bytes_ + numberOf8Bytes_, TRACE_ITEM_SIZE, numberOf2Bytes_);
84 // Atomic with relaxed order reason: data race with buffer_offset_ with no synchronization or ordering constraints
85 // imposed on other reads or writes
86 bufferOffset_.store(TRACE_HEADER_LENGTH, std::memory_order_relaxed);
87 }
88
89 Trace::~Trace() = default;
90
StartTracing(const char * traceFilename,size_t bufferSize)91 void Trace::StartTracing(const char *traceFilename, size_t bufferSize)
92 {
93 os::memory::LockHolder lock(g_traceLock);
94 if (singletonTrace_ != nullptr) {
95 LOG(ERROR, RUNTIME) << "method tracing is running, ignoring new request";
96 return;
97 }
98
99 PandaString fileName(traceFilename);
100 if (fileName.empty()) {
101 fileName = "method";
102 fileName = fileName + ConvertToString(std::to_string(RealTimeSecond()));
103 fileName += ".trace";
104 #ifdef PANDA_TARGET_MOBILE
105 fileName = "/data/misc/trace/" + fileName;
106 #endif // PANDA_TARGET_MOBILE
107 }
108
109 auto traceFile = MakePandaUnique<ark::os::unix::file::File>(
110 ark::os::file::Open(fileName, ark::os::file::Mode::READWRITECREATE).GetFd());
111 if (!traceFile->IsValid()) {
112 LOG(ERROR, RUNTIME) << "Cannot OPEN/CREATE the trace file " << fileName;
113 return;
114 }
115
116 panda_file::SourceLang lang = ark::plugins::RuntimeTypeToLang(Runtime::GetRuntimeType());
117 ctx_ = Runtime::GetCurrent()->GetLanguageContext(lang);
118
119 singletonTrace_ = ctx_.CreateTrace(std::move(traceFile), bufferSize);
120
121 Runtime::GetCurrent()->GetNotificationManager()->AddListener(singletonTrace_,
122 RuntimeNotificationManager::Event::METHOD_EVENTS);
123 isTracing_ = true;
124 }
125
StopTracing()126 void Trace::StopTracing()
127 {
128 os::memory::LockHolder lock(g_traceLock);
129 if (singletonTrace_ == nullptr) {
130 LOG(ERROR, RUNTIME) << "need to stop method tracing, but no method tracing is running";
131 } else {
132 Runtime::GetCurrent()->GetNotificationManager()->RemoveListener(
133 singletonTrace_, RuntimeNotificationManager::Event::METHOD_EVENTS);
134 singletonTrace_->SaveTracingData();
135 isTracing_ = false;
136 Runtime::GetCurrent()->GetInternalAllocator()->Delete(singletonTrace_);
137 singletonTrace_ = nullptr;
138 }
139 }
140
TriggerTracing()141 void Trace::TriggerTracing()
142 {
143 if (!isTracing_) {
144 Trace::StartTracing("", FILE_SIZE);
145 } else {
146 Trace::StopTracing();
147 }
148 }
149
RecordThreadsInfo(PandaOStringStream * os)150 void Trace::RecordThreadsInfo([[maybe_unused]] PandaOStringStream *os)
151 {
152 os::memory::LockHolder lock(threadInfoLock_);
153 for (const auto &info : threadInfoSet_) {
154 (*os) << info;
155 }
156 }
GetAverageTime()157 uint64_t Trace::GetAverageTime()
158 {
159 uint64_t begin = GetCpuMicroSecond();
160 for (uint32_t i = 0; i < loopNumber_; i++) {
161 GetCpuMicroSecond();
162 }
163 uint64_t delay = GetCpuMicroSecond() - begin;
164 return delay / divideNumber_;
165 }
166
RecordMethodsInfo(PandaOStringStream * os,const PandaSet<Method * > & calledMethods)167 void Trace::RecordMethodsInfo(PandaOStringStream *os, const PandaSet<Method *> &calledMethods)
168 {
169 for (const auto &method : calledMethods) {
170 (*os) << GetMethodDetailInfo(method);
171 }
172 }
173
WriteInfoToBuf(const ManagedThread * thread,Method * method,EventFlag event,uint32_t threadTime,uint32_t realTime)174 void Trace::WriteInfoToBuf(const ManagedThread *thread, Method *method, EventFlag event, uint32_t threadTime,
175 uint32_t realTime)
176 {
177 uint32_t writeAfterOffset;
178 uint32_t writeBeforeOffset;
179
180 // Atomic with relaxed order reason: data race with buffer_offset_ with no synchronization or ordering constraints
181 // imposed on other reads or writes
182 writeBeforeOffset = bufferOffset_.load(std::memory_order_relaxed);
183 do {
184 writeAfterOffset = writeBeforeOffset + TRACE_ITEM_SIZE;
185 if (bufferSize_ < static_cast<size_t>(writeAfterOffset)) {
186 overbrim_ = true;
187 return;
188 }
189 } while (!bufferOffset_.compare_exchange_weak(writeBeforeOffset, writeAfterOffset, std::memory_order_relaxed));
190
191 EventFlag flag = TRACE_METHOD_ENTER;
192 switch (event) {
193 case EventFlag::TRACE_METHOD_ENTER:
194 flag = TRACE_METHOD_ENTER;
195 break;
196 case EventFlag::TRACE_METHOD_EXIT:
197 flag = TRACE_METHOD_EXIT;
198 break;
199 case EventFlag::TRACE_METHOD_UNWIND:
200 flag = TRACE_METHOD_UNWIND;
201 break;
202 default:
203 LOG(ERROR, RUNTIME) << "unrecognized events" << std::endl;
204 }
205 uint32_t methodActionValue = EncodeMethodAndEventToId(method, flag);
206
207 uint8_t *ptr = buffer_.get() + writeBeforeOffset;
208 WriteDataByte(ptr, thread->GetId(), numberOf2Bytes_);
209 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
210 ptr += numberOf2Bytes_;
211 WriteDataByte(ptr, methodActionValue, numberOf4Bytes_);
212 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
213 ptr += numberOf4Bytes_;
214 WriteDataByte(ptr, threadTime, numberOf4Bytes_);
215 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
216 ptr += numberOf4Bytes_;
217 WriteDataByte(ptr, realTime, numberOf4Bytes_);
218 }
219
EncodeMethodAndEventToId(Method * method,EventFlag flag)220 uint32_t Trace::EncodeMethodAndEventToId(Method *method, EventFlag flag)
221 {
222 uint32_t methodActionId = (EncodeMethodToId(method) << ENCODE_EVENT_BITS) | flag;
223 ASSERT(method == DecodeIdToMethod(methodActionId));
224 return methodActionId;
225 }
226
EncodeMethodToId(Method * method)227 uint32_t Trace::EncodeMethodToId(Method *method)
228 {
229 os::memory::LockHolder lock(methodsLock_);
230 uint32_t methodIdValue = 0;
231 auto iter = methodIdPandamap_.find(method);
232 if (iter != methodIdPandamap_.end()) {
233 methodIdValue = iter->second;
234 } else {
235 methodIdValue = methodsCalledVector_.size();
236 methodsCalledVector_.push_back(method);
237 methodIdPandamap_.emplace(method, methodIdValue);
238 }
239 return methodIdValue;
240 }
241
DecodeIdToMethod(uint32_t id)242 Method *Trace::DecodeIdToMethod(uint32_t id)
243 {
244 os::memory::LockHolder lock(methodsLock_);
245 return methodsCalledVector_[id >> ENCODE_EVENT_BITS];
246 }
247
GetUniqueMethods(size_t bufferLength,PandaSet<Method * > * calledMethodsSet)248 void Trace::GetUniqueMethods(size_t bufferLength, PandaSet<Method *> *calledMethodsSet)
249 {
250 uint8_t *dataBegin = buffer_.get() + TRACE_HEADER_LENGTH;
251 uint8_t *dataEnd = buffer_.get() + bufferLength;
252
253 while (dataBegin < dataEnd) {
254 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
255 uint32_t decodedData = GetDataFromBuffer(dataBegin + numberOf2Bytes_, numberOf4Bytes_);
256 Method *method = DecodeIdToMethod(decodedData);
257 calledMethodsSet->insert(method);
258 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
259 dataBegin += TRACE_ITEM_SIZE;
260 }
261 }
262
SaveTracingData()263 void Trace::SaveTracingData()
264 {
265 uint64_t elapsed = SystemMicroSecond() - traceStartTime_;
266 PandaOStringStream ostream;
267 ostream << TRACE_STAR_CHAR << "version\n";
268 ostream << TRACE_VERSION << "\n";
269 ostream << "data-file-overflow=" << (overbrim_ ? "true" : "false") << "\n";
270 ostream << "clock=dual\n";
271 ostream << "elapsed-time-usec=" << elapsed << "\n";
272
273 size_t bufEnd;
274 // Atomic with relaxed order reason: data race with buffer_offset_ with no synchronization or ordering constraints
275 // imposed on other reads or writes
276 bufEnd = bufferOffset_.load(std::memory_order_relaxed);
277 size_t numRecords = (bufEnd - TRACE_HEADER_LENGTH) / TRACE_ITEM_SIZE;
278 ostream << "num-method-calls=" << numRecords << "\n";
279 ostream << "clock-call-overhead-nsec=" << GetAverageTime() << "\n";
280 ostream << "pid=" << getpid() << "\n";
281 ostream << "vm=panda\n";
282
283 ostream << TRACE_STAR_CHAR << "threads\n";
284 RecordThreadsInfo(&ostream);
285 ostream << TRACE_STAR_CHAR << "methods\n";
286
287 PandaSet<Method *> calledMethodsSet;
288 GetUniqueMethods(bufEnd, &calledMethodsSet);
289
290 RecordMethodsInfo(&ostream, calledMethodsSet);
291 ostream << TRACE_STAR_CHAR << "end\n";
292 PandaString methodsInfo(ostream.str());
293
294 traceFile_->WriteAll(reinterpret_cast<const void *>(methodsInfo.c_str()), methodsInfo.length());
295 traceFile_->WriteAll(buffer_.get(), bufEnd);
296 traceFile_->Close();
297 }
298
MethodEntry(ManagedThread * thread,Method * method)299 void Trace::MethodEntry(ManagedThread *thread, Method *method)
300 {
301 os::memory::LockHolder lock(threadInfoLock_);
302 uint32_t threadTime = 0;
303 uint32_t realTime = 0;
304 GetTimes(&threadTime, &realTime);
305 WriteInfoToBuf(thread, method, EventFlag::TRACE_METHOD_ENTER, threadTime, realTime);
306
307 PandaOStringStream os;
308 auto threadName = GetThreadName(thread);
309 os << thread->GetId() << "\t" << threadName << "\n";
310 PandaString info = os.str();
311 threadInfoSet_.insert(info);
312 }
313
MethodExit(ManagedThread * thread,Method * method)314 void Trace::MethodExit(ManagedThread *thread, Method *method)
315 {
316 uint32_t threadTime = 0;
317 uint32_t realTime = 0;
318 GetTimes(&threadTime, &realTime);
319 WriteInfoToBuf(thread, method, EventFlag::TRACE_METHOD_EXIT, threadTime, realTime);
320 }
321
GetTimes(uint32_t * threadTime,uint32_t * realTime)322 void Trace::GetTimes(uint32_t *threadTime, uint32_t *realTime)
323 {
324 *threadTime = GetCpuMicroSecond() - baseCpuTime_;
325 *realTime = SystemMicroSecond() - traceStartTime_;
326 }
327
328 } // namespace ark
329