• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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, &current);
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, &current);
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, &current);
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